• 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/idl.h"
24 #include "flatbuffers/util.h"
25 #include "idl_gen_kotlin.h"
26 #include "idl_namer.h"
27 
28 namespace flatbuffers {
29 
30 namespace kotlin {
31 
32 namespace {
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 = "    ";
KotlinKeywords()41 static std::set<std::string> KotlinKeywords() {
42   return { "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 
KotlinDefaultConfig()49 static Namer::Config KotlinDefaultConfig() {
50   return { /*types=*/Case::kKeep,
51            /*constants=*/Case::kUpperCamel,
52            /*methods=*/Case::kLowerCamel,
53            /*functions=*/Case::kKeep,
54            /*fields=*/Case::kLowerCamel,
55            /*variables=*/Case::kLowerCamel,
56            /*variants=*/Case::kUpperCamel,
57            /*enum_variant_seperator=*/"",  // I.e. Concatenate.
58            /*escape_keywords=*/Namer::Config::Escape::AfterConvertingCase,
59            /*namespaces=*/Case::kLowerCamel,
60            /*namespace_seperator=*/".",
61            /*object_prefix=*/"",
62            /*object_suffix=*/"T",
63            /*keyword_prefix=*/"",
64            /*keyword_suffix=*/"E",
65            /*filenames=*/Case::kUpperCamel,
66            /*directories=*/Case::kLowerCamel,
67            /*output_path=*/"",
68            /*filename_suffix=*/"",
69            /*filename_extension=*/".kt" };
70 }
71 }  // namespace
72 
73 class KotlinKMPGenerator : public BaseGenerator {
74  public:
KotlinKMPGenerator(const Parser & parser,const std::string & path,const std::string & file_name)75   KotlinKMPGenerator(const Parser &parser, const std::string &path,
76                      const std::string &file_name)
77       : BaseGenerator(parser, path, file_name, "", ".", "kt"),
78         namer_(WithFlagOptions(KotlinDefaultConfig(), parser.opts, path),
79                KotlinKeywords()) {}
80 
81   KotlinKMPGenerator &operator=(const KotlinKMPGenerator &);
generate()82   bool generate() FLATBUFFERS_OVERRIDE {
83     std::string one_file_code;
84 
85     for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
86          ++it) {
87       CodeWriter enumWriter(ident_pad);
88       auto &enum_def = **it;
89 
90       GenEnum(enum_def, enumWriter);
91       enumWriter += "";
92       GenEnumOffsetAlias(enum_def, enumWriter);
93 
94       if (parser_.opts.one_file) {
95         one_file_code += enumWriter.ToString();
96       } else {
97         if (!SaveType(namer_.EscapeKeyword(enum_def.name),
98                       *enum_def.defined_namespace, enumWriter.ToString(), true))
99           return false;
100       }
101     }
102 
103     for (auto it = parser_.structs_.vec.begin();
104          it != parser_.structs_.vec.end(); ++it) {
105       CodeWriter structWriter(ident_pad);
106       auto &struct_def = **it;
107 
108       GenStruct(struct_def, structWriter, parser_.opts);
109       structWriter += "";
110       GenStructOffsetAlias(struct_def, structWriter);
111 
112       if (parser_.opts.one_file) {
113         one_file_code += structWriter.ToString();
114       } else {
115         if (!SaveType(namer_.EscapeKeyword(struct_def.name),
116                       *struct_def.defined_namespace, structWriter.ToString(),
117                       true))
118           return false;
119       }
120     }
121 
122     if (parser_.opts.one_file) {
123       return SaveType(file_name_, *parser_.current_namespace_, one_file_code,
124                       true);
125     }
126     return true;
127   }
128 
TypeInNameSpace(const Namespace * ns,const std::string & name="") const129   std::string TypeInNameSpace(const Namespace *ns,
130                               const std::string &name = "") const {
131     auto qualified = namer_.Namespace(*ns);
132     return qualified.empty() ? name : qualified + qualifying_separator_ + name;
133   }
134 
TypeInNameSpace(const Definition & def,const std::string & suffix="") const135   std::string TypeInNameSpace(const Definition &def,
136                               const std::string &suffix = "") const {
137     return TypeInNameSpace(def.defined_namespace, def.name + suffix);
138   }
139 
140   // Save out the generated code for a single class while adding
141   // declaration boilerplate.
SaveType(const std::string & defname,const Namespace & ns,const std::string & classcode,bool needs_includes) const142   bool SaveType(const std::string &defname, const Namespace &ns,
143                 const std::string &classcode, bool needs_includes) const {
144     if (!classcode.length()) return true;
145 
146     std::string code =
147         "// " + std::string(FlatBuffersGeneratedWarning()) + "\n\n";
148     auto qualified = ns.GetFullyQualifiedName("");
149     std::string namespace_name = namer_.Namespace(ns);
150     if (!namespace_name.empty()) {
151       code += "package " + namespace_name;
152       code += "\n\n";
153     }
154     if (needs_includes) { code += "import com.google.flatbuffers.kotlin.*\n"; }
155     code += "import kotlin.jvm.JvmInline\n";
156     code += classcode;
157     const std::string dirs =
158         namer_.Directories(ns, SkipDir::None, Case::kUnknown);
159     EnsureDirExists(dirs);
160     const std::string filename =
161         dirs + namer_.File(defname, /*skips=*/SkipFile::Suffix);
162     return SaveFile(filename.c_str(), code, false);
163   }
164 
IsEnum(const Type & type)165   static bool IsEnum(const Type &type) {
166     return type.enum_def != nullptr && IsInteger(type.base_type);
167   }
168 
GenerateKotlinPrimiteArray(const Type & type) const169   std::string GenerateKotlinPrimiteArray(const Type &type) const {
170     if (IsScalar(type.base_type) && !IsEnum(type)) { return GenType(type); }
171 
172     if (IsEnum(type) || type.base_type == BASE_TYPE_UTYPE) {
173       return TypeInNameSpace(type.enum_def->defined_namespace,
174                              namer_.Type(*type.enum_def));
175     }
176     switch (type.base_type) {
177       case BASE_TYPE_STRUCT:
178         return "Offset<" + TypeInNameSpace(*type.struct_def) + ">";
179       case BASE_TYPE_UNION: return "UnionOffset";
180       case BASE_TYPE_STRING: return "Offset<String>";
181       case BASE_TYPE_UTYPE: return "Offset<UByte>";
182       default: return "Offset<" + GenTypeBasic(type.element) + ">";
183     }
184   }
185 
GenerateKotlinOffsetArray(const Type & type) const186   std::string GenerateKotlinOffsetArray(const Type &type) const {
187     if (IsScalar(type.base_type) && !IsEnum(type)) {
188       return GenType(type) + "Array";
189     }
190 
191     if (IsEnum(type) || type.base_type == BASE_TYPE_UTYPE) {
192       return TypeInNameSpace(type.enum_def->defined_namespace,
193                              namer_.Type(*type.enum_def) + "Array");
194     }
195     switch (type.base_type) {
196       case BASE_TYPE_STRUCT:
197         return TypeInNameSpace(*type.struct_def) + "OffsetArray";
198       case BASE_TYPE_UNION: return "UnionOffsetArray";
199       case BASE_TYPE_STRING: return "StringOffsetArray";
200       case BASE_TYPE_UTYPE: return "UByteArray";
201       default: return GenTypeBasic(type.element) + "OffsetArray";
202     }
203   }
204 
GenTypeBasic(const BaseType & type) const205   std::string GenTypeBasic(const BaseType &type) const {
206     switch (type) {
207       case BASE_TYPE_NONE:
208       case BASE_TYPE_UTYPE: return "UByte";
209       case BASE_TYPE_BOOL: return "Boolean";
210       case BASE_TYPE_CHAR: return "Byte";
211       case BASE_TYPE_UCHAR: return "UByte";
212       case BASE_TYPE_SHORT: return "Short";
213       case BASE_TYPE_USHORT: return "UShort";
214       case BASE_TYPE_INT: return "Int";
215       case BASE_TYPE_UINT: return "UInt";
216       case BASE_TYPE_LONG: return "Long";
217       case BASE_TYPE_ULONG: return "ULong";
218       case BASE_TYPE_FLOAT: return "Float";
219       case BASE_TYPE_DOUBLE: return "Double";
220       case BASE_TYPE_STRING:
221       case BASE_TYPE_STRUCT: return "Offset";
222       case BASE_TYPE_UNION: return "UnionOffset";
223       case BASE_TYPE_VECTOR:
224       case BASE_TYPE_ARRAY: return "VectorOffset";
225       // VECTOR64 not supported
226       case BASE_TYPE_VECTOR64: FLATBUFFERS_ASSERT(0);
227     }
228     return "Int";
229   }
230 
GenType(const Type & type) const231   std::string GenType(const Type &type) const {
232     auto base_type = GenTypeBasic(type.base_type);
233 
234     if (IsEnum(type) || type.base_type == BASE_TYPE_UTYPE) {
235       return TypeInNameSpace(type.enum_def->defined_namespace,
236                              namer_.Type(*type.enum_def));
237     }
238     switch (type.base_type) {
239       case BASE_TYPE_ARRAY:
240       case BASE_TYPE_VECTOR: {
241         switch (type.element) {
242           case BASE_TYPE_STRUCT:
243             return base_type + "<" + TypeInNameSpace(*type.struct_def) + ">";
244           case BASE_TYPE_UNION:
245             return base_type + "<" + GenTypeBasic(type.element) + ">";
246           case BASE_TYPE_STRING: return base_type + "<String>";
247           case BASE_TYPE_UTYPE: return base_type + "<UByte>";
248           default: return base_type + "<" + GenTypeBasic(type.element) + ">";
249         }
250       }
251       case BASE_TYPE_STRUCT:
252         return base_type + "<" + TypeInNameSpace(*type.struct_def) + ">";
253       case BASE_TYPE_STRING: return base_type + "<String>";
254       case BASE_TYPE_UNION: return base_type;
255       default: return base_type;
256     }
257     // clang-format on
258   }
259 
GenTypePointer(const Type & type) const260   std::string GenTypePointer(const Type &type) const {
261     switch (type.base_type) {
262       case BASE_TYPE_STRING: return "String";
263       case BASE_TYPE_VECTOR: return GenTypeGet(type.VectorType());
264       case BASE_TYPE_STRUCT: return TypeInNameSpace(*type.struct_def);
265       default: return "Table";
266     }
267   }
268 
269   // with the addition of optional scalar types,
270   // we are adding the nullable '?' operator to return type of a field.
GetterReturnType(const FieldDef & field) const271   std::string GetterReturnType(const FieldDef &field) const {
272     auto base_type = field.value.type.base_type;
273 
274     auto r_type = GenTypeGet(field.value.type);
275     if (field.IsScalarOptional() ||
276         // string, structs and unions
277         (base_type == BASE_TYPE_STRING || base_type == BASE_TYPE_STRUCT ||
278          base_type == BASE_TYPE_UNION) ||
279         // vector of anything not scalar
280         (base_type == BASE_TYPE_VECTOR &&
281          !IsScalar(field.value.type.VectorType().base_type))) {
282       r_type += "?";
283     }
284     return r_type;
285   }
286 
GenTypeGet(const Type & type) const287   std::string GenTypeGet(const Type &type) const {
288     return IsScalar(type.base_type) ? GenType(type) : GenTypePointer(type);
289   }
290 
GenEnumDefaultValue(const FieldDef & field) const291   std::string GenEnumDefaultValue(const FieldDef &field) const {
292     auto &value = field.value;
293     FLATBUFFERS_ASSERT(value.type.enum_def);
294     auto &enum_def = *value.type.enum_def;
295     auto enum_val = enum_def.FindByValue(value.constant);
296     return enum_val ? (TypeInNameSpace(enum_def) + "." + enum_val->name)
297                     : value.constant;
298   }
299 
300   // differently from GenDefaultValue, the default values are meant
301   // to be inserted in the buffer as the object is building.
GenDefaultBufferValue(const FieldDef & field) const302   std::string GenDefaultBufferValue(const FieldDef &field) const {
303     auto &value = field.value;
304     auto base_type = value.type.base_type;
305     auto field_name = field.name;
306     std::string suffix = IsScalar(base_type) ? LiteralSuffix(value.type) : "";
307     if (field.IsScalarOptional()) { return "null"; }
308     if (IsFloat(base_type)) {
309       auto val = KotlinFloatGen.GenFloatConstant(field);
310       if (base_type == BASE_TYPE_DOUBLE && val.back() == 'f') {
311         val.pop_back();
312       }
313       return val;
314     }
315 
316     if (base_type == BASE_TYPE_BOOL) {
317       return value.constant == "0" ? "false" : "true";
318     }
319 
320     if (IsEnum(field.value.type)) {
321       return value.constant + suffix;
322     } else if ((IsVector(field.value.type) &&
323                 field.value.type.element == BASE_TYPE_UTYPE) ||
324                (IsVector(field.value.type) &&
325                 field.value.type.VectorType().base_type == BASE_TYPE_UNION)) {
326       return value.constant;
327     } else {
328       return value.constant + suffix;
329     }
330   }
331 
GenDefaultValue(const FieldDef & field) const332   std::string GenDefaultValue(const FieldDef &field) const {
333     auto &value = field.value;
334     auto base_type = value.type.base_type;
335     auto field_name = field.name;
336     std::string suffix = LiteralSuffix(value.type);
337     if (field.IsScalarOptional()) { return "null"; }
338     if (IsFloat(base_type)) {
339       auto val = KotlinFloatGen.GenFloatConstant(field);
340       if (base_type == BASE_TYPE_DOUBLE && val.back() == 'f') {
341         val.pop_back();
342       }
343       return val;
344     }
345 
346     if (base_type == BASE_TYPE_BOOL) {
347       return value.constant == "0" ? "false" : "true";
348     }
349 
350     if (IsEnum(field.value.type) ||
351         (IsVector(field.value.type) && IsEnum(field.value.type.VectorType()))) {
352       return WrapEnumValue(field.value.type, value.constant + suffix);
353     }
354 
355     if (IsVector(field.value.type) &&
356         (field.value.type.VectorType().base_type == BASE_TYPE_UNION ||
357          field.value.type.VectorType().base_type == BASE_TYPE_STRUCT ||
358          field.value.type.VectorType().base_type == BASE_TYPE_STRING)) {
359       return "null";
360     }
361     if (IsVector(field.value.type)) {
362       switch (field.value.type.element) {
363         case BASE_TYPE_UTYPE:
364           return namer_.Type(*field.value.type.enum_def) + "(" +
365                  value.constant + suffix + ")";
366         case BASE_TYPE_UNION:
367         case BASE_TYPE_STRUCT:
368         case BASE_TYPE_STRING: return "null";
369         case BASE_TYPE_BOOL: return value.constant == "0" ? "false" : "true";
370         case BASE_TYPE_FLOAT: return value.constant + "f";
371         case BASE_TYPE_DOUBLE: {
372           return value.constant + ".toDouble()";
373         }
374         default: return value.constant + suffix;
375       }
376     }
377     return value.constant + suffix;
378   }
379 
GenEnum(EnumDef & enum_def,CodeWriter & writer) const380   void GenEnum(EnumDef &enum_def, CodeWriter &writer) const {
381     if (enum_def.generated) return;
382 
383     GenerateComment(enum_def.doc_comment, writer, &comment_config);
384     auto enum_type = namer_.Type(enum_def);
385     auto field_type = GenTypeBasic(enum_def.underlying_type.base_type);
386     writer += "@Suppress(\"unused\")";
387     writer += "@JvmInline";
388     writer += "value class " + enum_type + " (val value: " + field_type + ") {";
389     writer.IncrementIdentLevel();
390 
391     GenerateCompanionObject(writer, [&]() {
392       // Write all properties
393       auto vals = enum_def.Vals();
394 
395       for (auto it = vals.begin(); it != vals.end(); ++it) {
396         auto &ev = **it;
397         auto val = enum_def.ToString(ev);
398         auto suffix = LiteralSuffix(enum_def.underlying_type);
399         writer.SetValue("name", namer_.Variant(ev));
400         writer.SetValue("type", enum_type);
401         writer.SetValue("val", val + suffix);
402         GenerateComment(ev.doc_comment, writer, &comment_config);
403         writer += "val {{name}} = {{type}}({{val}})";
404       }
405 
406       // Generate a generate string table for enum values.
407       // Problem is, if values are very sparse that could generate really
408       // big tables. Ideally in that case we generate a map lookup
409       // instead, but for the moment we simply don't output a table at all.
410       auto range = enum_def.Distance();
411       // Average distance between values above which we consider a table
412       // "too sparse". Change at will.
413       static const uint64_t kMaxSparseness = 5;
414       if (range / static_cast<uint64_t>(enum_def.size()) < kMaxSparseness) {
415         GeneratePropertyOneLine(writer, "names", "Array<String>", [&]() {
416           writer += "arrayOf(\\";
417           auto val = enum_def.Vals().front();
418           for (auto it = vals.begin(); it != vals.end(); ++it) {
419             auto ev = *it;
420             for (auto k = enum_def.Distance(val, ev); k > 1; --k)
421               writer += "\"\", \\";
422             val = ev;
423             writer += "\"" + (*it)->name + "\"\\";
424             if (it + 1 != vals.end()) { writer += ", \\"; }
425           }
426           writer += ")";
427         });
428         std::string e_param = "e: " + enum_type;
429         GenerateFunOneLine(
430             writer, "name", e_param, "String",
431             [&]() {
432               writer += "names[e.value.toInt()\\";
433               if (enum_def.MinValue()->IsNonZero())
434                 writer += " - " + namer_.Variant(*enum_def.MinValue()) +
435                           ".value.toInt()\\";
436               writer += "]";
437             },
438             parser_.opts.gen_jvmstatic);
439       }
440     });
441     writer.DecrementIdentLevel();
442     writer += "}";
443   }
444 
445   // Returns the function name that is able to read a value of the given type.
ByteBufferGetter(const Type & type,std::string bb_var_name) const446   std::string ByteBufferGetter(const Type &type,
447                                std::string bb_var_name) const {
448     switch (type.base_type) {
449       case BASE_TYPE_STRING: return "string";
450       case BASE_TYPE_STRUCT: return "__struct";
451       case BASE_TYPE_UNION: return "union";
452       case BASE_TYPE_VECTOR:
453         return ByteBufferGetter(type.VectorType(), bb_var_name);
454       case BASE_TYPE_INT: return bb_var_name + ".getInt";
455       case BASE_TYPE_UINT: return bb_var_name + ".getUInt";
456       case BASE_TYPE_SHORT: return bb_var_name + ".getShort";
457       case BASE_TYPE_USHORT: return bb_var_name + ".getUShort";
458       case BASE_TYPE_ULONG: return bb_var_name + ".getULong";
459       case BASE_TYPE_LONG: return bb_var_name + ".getLong";
460       case BASE_TYPE_FLOAT: return bb_var_name + ".getFloat";
461       case BASE_TYPE_DOUBLE: return bb_var_name + ".getDouble";
462       case BASE_TYPE_UTYPE:
463       case BASE_TYPE_UCHAR: return bb_var_name + ".getUByte";
464       case BASE_TYPE_CHAR:
465       case BASE_TYPE_NONE: return bb_var_name + ".get";
466       case BASE_TYPE_BOOL: return "0.toByte() != " + bb_var_name + ".get";
467       default: return bb_var_name + "." + namer_.Method("get", GenType(type));
468     }
469   }
470 
471   // 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) const472   std::string GenLookupByKey(flatbuffers::FieldDef *key_field,
473                              const std::string &bb_var_name,
474                              const char *num = nullptr) const {
475     auto type = key_field->value.type;
476     return ByteBufferGetter(type, bb_var_name) + "(" +
477            GenOffsetGetter(key_field, num) + ")";
478   }
479 
480   // Returns the method name for use with add/put calls.
GenMethod(const Type & type)481   static std::string GenMethod(const Type &type) {
482     return IsStruct(type) ? "Struct" : "";
483   }
484 
485   // Recursively generate arguments for a constructor, to deal with nested
486   // structs.
GenStructArgs(const StructDef & struct_def,CodeWriter & writer,const char * nameprefix) const487   void GenStructArgs(const StructDef &struct_def, CodeWriter &writer,
488                      const char *nameprefix) const {
489     for (auto it = struct_def.fields.vec.begin();
490          it != struct_def.fields.vec.end(); ++it) {
491       auto &field = **it;
492       if (IsStruct(field.value.type)) {
493         // Generate arguments for a struct inside a struct. To ensure
494         // names don't clash, and to make it obvious these arguments are
495         // constructing a nested struct, prefix the name with the field
496         // name.
497         GenStructArgs(*field.value.type.struct_def, writer,
498                       (nameprefix + (field.name + "_")).c_str());
499       } else {
500         writer += std::string(", ") + nameprefix + "\\";
501         writer += namer_.Field(field) + ": \\";
502         writer += GenType(field.value.type) + "\\";
503       }
504     }
505   }
506 
507   // Recusively generate struct construction statements of the form:
508   // builder.putType(name);
509   // and insert manual padding.
GenStructBody(const StructDef & struct_def,CodeWriter & writer,const char * nameprefix) const510   void GenStructBody(const StructDef &struct_def, CodeWriter &writer,
511                      const char *nameprefix) const {
512     writer.SetValue("align", NumToString(struct_def.minalign));
513     writer.SetValue("size", NumToString(struct_def.bytesize));
514     writer += "builder.prep({{align}}, {{size}})";
515     auto fields_vec = struct_def.fields.vec;
516     for (auto it = fields_vec.rbegin(); it != fields_vec.rend(); ++it) {
517       auto &field = **it;
518 
519       if (field.padding) {
520         writer.SetValue("pad", NumToString(field.padding));
521         writer += "builder.pad({{pad}})";
522       }
523       if (IsStruct(field.value.type)) {
524         GenStructBody(*field.value.type.struct_def, writer,
525                       (nameprefix + (field.name + "_")).c_str());
526       } else {
527         auto suffix = IsEnum(field.value.type) ? ".value" : "";
528         writer.SetValue("type", GenMethod(field.value.type));
529         writer.SetValue("argname",
530                         nameprefix + namer_.Variable(field) + suffix);
531         writer += "builder.put{{type}}({{argname}})";
532       }
533     }
534   }
535 
GenOffsetGetter(flatbuffers::FieldDef * key_field,const char * num=nullptr) const536   std::string GenOffsetGetter(flatbuffers::FieldDef *key_field,
537                               const char *num = nullptr) const {
538     std::string key_offset =
539         "offset(" + NumToString(key_field->value.offset) + ", ";
540     if (num) {
541       key_offset += num;
542       key_offset += ", buffer)";
543     } else {
544       key_offset += "(bb.capacity - tableOffset).toOffset<Int>(), bb)";
545     }
546     return key_offset;
547   }
548 
StructHasUnsignedField(StructDef & struct_def)549   bool StructHasUnsignedField(StructDef &struct_def) {
550     auto vec = struct_def.fields.vec;
551     for (auto it = vec.begin(); it != vec.end(); ++it) {
552       auto &field = **it;
553       if (IsUnsigned(field.value.type.base_type)) { return true; }
554     }
555     return false;
556   }
557 
558   // This method generate alias types for offset arrays. We need it
559   // to avoid unboxing/boxing of offsets when put into an array.
560   // e.g:
561   // Array<Offset<Monster>> generates boxing.
562   // So we creates a new type to avoid it:
563   // typealias MonterOffsetArray = IntArray
GenStructOffsetAlias(StructDef & struct_def,CodeWriter & writer) const564   void GenStructOffsetAlias(StructDef &struct_def, CodeWriter &writer) const {
565     if (struct_def.generated) return;
566     auto name = namer_.Type(struct_def);
567     // This assumes offset as Ints always.
568     writer += "typealias " + name + "OffsetArray = OffsetArray<" + name + ">";
569 
570     // public inline fun <T> MonsterOffsetArray(size: Int, crossinline call:
571     // (Int) -> Offset<T>): OffsetArray<T> {
572     //  return OffsetArray(IntArray(size) { call(it).value })
573     // }
574     writer += "";
575     writer += "inline fun " + name +
576               "OffsetArray(size: Int, crossinline call: (Int) -> Offset<" +
577               name + ">): " + name + "OffsetArray =";
578     writer.IncrementIdentLevel();
579     writer += name + "OffsetArray(IntArray(size) { call(it).value })";
580   }
581 
582   // This method generate alias types for offset arrays. We need it
583   // to avoid unboxing/boxing of offsets when put into an array.
584   // e.g:
585   // Array<Offset<Monster>> generates boxing.
586   // So we creates a new type to avoid it:
587   // typealias MonterOffsetArray = IntArray
GenEnumOffsetAlias(EnumDef & enum_def,CodeWriter & writer) const588   void GenEnumOffsetAlias(EnumDef &enum_def, CodeWriter &writer) const {
589     if (enum_def.generated) return;
590     // This assumes offset as Ints always.
591     writer += "typealias " + namer_.Type(enum_def) +
592               "Array = " + GenTypeBasic(enum_def.underlying_type.base_type) +
593               "Array";
594   }
595 
GenStruct(StructDef & struct_def,CodeWriter & writer,IDLOptions options) const596   void GenStruct(StructDef &struct_def, CodeWriter &writer,
597                  IDLOptions options) const {
598     if (struct_def.generated) return;
599 
600     GenerateComment(struct_def.doc_comment, writer, &comment_config);
601     auto fixed = struct_def.fixed;
602 
603     writer.SetValue("struct_name", namer_.Type(struct_def));
604     writer.SetValue("superclass", fixed ? "Struct" : "Table");
605 
606     writer += "@Suppress(\"unused\")";
607     writer += "class {{struct_name}} : {{superclass}}() {\n";
608 
609     writer.IncrementIdentLevel();
610 
611     {
612       auto esc_type = namer_.EscapeKeyword(struct_def.name);
613       // Generate the init() method that sets the field in a pre-existing
614       // accessor object. This is to allow object reuse.
615       GenerateFunOneLine(writer, "init", "i: Int, buffer: ReadWriteBuffer",
616                          esc_type, [&]() { writer += "reset(i, buffer)"; });
617       writer += "";  // line break
618 
619       // Generate all getters
620       GenerateStructGetters(struct_def, writer);
621 
622       // Generate Static Fields
623       GenerateCompanionObject(writer, [&]() {
624         if (!struct_def.fixed) {
625           FieldDef *key_field = nullptr;
626 
627           // Generate version check method.
628           // Force compile time error if not using the same version
629           // runtime.
630           GenerateFunOneLine(
631               writer, "validateVersion", "", "",
632               [&]() { writer += "VERSION_2_0_8"; }, options.gen_jvmstatic);
633 
634           writer += "";
635           GenerateGetRootAsAccessors(namer_.Type(struct_def), writer, options);
636 
637           writer += "";
638           GenerateBufferHasIdentifier(struct_def, writer, options);
639 
640           writer += "";
641           GenerateTableCreator(struct_def, writer, options);
642 
643           GenerateStartStructMethod(struct_def, writer, options);
644 
645           // Static Add for fields
646           auto fields = struct_def.fields.vec;
647           int field_pos = -1;
648           for (auto it = fields.begin(); it != fields.end(); ++it) {
649             auto &field = **it;
650             field_pos++;
651             if (field.deprecated) continue;
652             if (field.key) key_field = &field;
653             writer += "";
654             GenerateAddField(NumToString(field_pos), field, writer, options);
655             if (IsVector(field.value.type)) {
656               auto vector_type = field.value.type.VectorType();
657               if (!IsStruct(vector_type)) {
658                 writer += "";
659                 GenerateCreateVectorField(field, writer, options);
660               }
661               writer += "";
662               GenerateStartVectorField(field, writer, options);
663             }
664           }
665 
666           writer += "";
667           GenerateEndStructMethod(struct_def, writer, options);
668           auto file_identifier = parser_.file_identifier_;
669           if (parser_.root_struct_def_ == &struct_def) {
670             writer += "";
671             GenerateFinishStructBuffer(struct_def, file_identifier, writer,
672                                        options);
673             writer += "";
674             GenerateFinishSizePrefixed(struct_def, file_identifier, writer,
675                                        options);
676           }
677 
678           if (struct_def.has_key) {
679             writer += "";
680             GenerateLookupByKey(key_field, struct_def, writer, options);
681           }
682         } else {
683           writer += "";
684           GenerateStaticConstructor(struct_def, writer, options);
685         }
686       });
687     }
688 
689     // class closing
690     writer.DecrementIdentLevel();
691     writer += "}";
692   }
693 
694   // TODO: move key_field to reference instead of pointer
GenerateLookupByKey(FieldDef * key_field,StructDef & struct_def,CodeWriter & writer,const IDLOptions options) const695   void GenerateLookupByKey(FieldDef *key_field, StructDef &struct_def,
696                            CodeWriter &writer, const IDLOptions options) const {
697     std::stringstream params;
698     params << "obj: " << namer_.Type(struct_def) << "?"
699            << ", ";
700     params << "vectorLocation: Int, ";
701     params << "key: " << GenTypeGet(key_field->value.type) << ", ";
702     params << "bb: ReadWriteBuffer";
703 
704     auto statements = [&]() {
705       auto base_type = key_field->value.type.base_type;
706       writer.SetValue("struct_name", namer_.Type(struct_def));
707       if (base_type == BASE_TYPE_STRING) {
708         writer += "val byteKey = key.encodeToByteArray()";
709       }
710       writer += "var span = bb.getInt(vectorLocation - 4)";
711       writer += "var start = 0";
712       writer += "while (span != 0) {";
713       writer.IncrementIdentLevel();
714       writer += "var middle = span / 2";
715       writer +=
716           "val tableOffset = indirect(vector"
717           "Location + 4 * (start + middle), bb)";
718       if (IsString(key_field->value.type)) {
719         writer += "val comp = compareStrings(\\";
720         writer += GenOffsetGetter(key_field) + "\\";
721         writer += ", byteKey, bb)";
722       } else {
723         auto get_val = GenLookupByKey(key_field, "bb");
724         writer += "val value = " + get_val;
725         writer += "val comp = value.compareTo(key)";
726       }
727       writer += "when {";
728       writer.IncrementIdentLevel();
729       writer += "comp > 0 -> span = middle";
730       writer += "comp < 0 -> {";
731       writer.IncrementIdentLevel();
732       writer += "middle++";
733       writer += "start += middle";
734       writer += "span -= middle";
735       writer.DecrementIdentLevel();
736       writer += "}";  // end comp < 0
737       writer += "else -> {";
738       writer.IncrementIdentLevel();
739       writer += "return (obj ?: {{struct_name}}()).init(tableOffset, bb)";
740       writer.DecrementIdentLevel();
741       writer += "}";  // end else
742       writer.DecrementIdentLevel();
743       writer += "}";  // end when
744       writer.DecrementIdentLevel();
745       writer += "}";  // end while
746       writer += "return null";
747     };
748     GenerateFun(writer, "lookupByKey", params.str(),
749                 namer_.Type(struct_def) + "?", statements,
750                 options.gen_jvmstatic);
751   }
752 
GenerateFinishSizePrefixed(StructDef & struct_def,const std::string & identifier,CodeWriter & writer,const IDLOptions options) const753   void GenerateFinishSizePrefixed(StructDef &struct_def,
754                                   const std::string &identifier,
755                                   CodeWriter &writer,
756                                   const IDLOptions options) const {
757     auto id = identifier.length() > 0 ? ", \"" + identifier + "\"" : "";
758     auto gen_type = "Offset<" + namer_.Type(struct_def.name) + ">";
759     auto params = "builder: FlatBufferBuilder, offset: " + gen_type;
760     auto method_name =
761         namer_.LegacyJavaMethod2("finishSizePrefixed", struct_def, "Buffer");
762     GenerateFunOneLine(
763         writer, method_name, params, "",
764         [&]() { writer += "builder.finishSizePrefixed(offset" + id + ")"; },
765         options.gen_jvmstatic);
766   }
GenerateFinishStructBuffer(StructDef & struct_def,const std::string & identifier,CodeWriter & writer,const IDLOptions options) const767   void GenerateFinishStructBuffer(StructDef &struct_def,
768                                   const std::string &identifier,
769                                   CodeWriter &writer,
770                                   const IDLOptions options) const {
771     auto id = identifier.length() > 0 ? ", \"" + identifier + "\"" : "";
772     auto gen_type = "Offset<" + namer_.Type(struct_def.name) + ">";
773     auto params = "builder: FlatBufferBuilder, offset: " + gen_type;
774     auto method_name =
775         namer_.LegacyKotlinMethod("finish", struct_def, "Buffer");
776     GenerateFunOneLine(
777         writer, method_name, params, "",
778         [&]() { writer += "builder.finish(offset" + id + ")"; },
779         options.gen_jvmstatic);
780   }
781 
GenerateEndStructMethod(StructDef & struct_def,CodeWriter & writer,const IDLOptions options) const782   void GenerateEndStructMethod(StructDef &struct_def, CodeWriter &writer,
783                                const IDLOptions options) const {
784     // Generate end{{TableName}}(builder: FlatBufferBuilder) method
785     auto name = namer_.Method("end", struct_def.name);
786     auto params = "builder: FlatBufferBuilder";
787     auto returns = "Offset<" + namer_.Type(struct_def) + '>';
788     auto field_vec = struct_def.fields.vec;
789 
790     GenerateFun(
791         writer, name, params, returns,
792         [&]() {
793           writer += "val o: " + returns + " = builder.endTable()";
794           writer.IncrementIdentLevel();
795           for (auto it = field_vec.begin(); it != field_vec.end(); ++it) {
796             auto &field = **it;
797             if (field.deprecated || !field.IsRequired()) { continue; }
798             writer.SetValue("offset", NumToString(field.value.offset));
799             writer.SetValue("field_name", field.name);
800             writer += "builder.required(o, {{offset}}, \"{{field_name}}\")";
801           }
802           writer.DecrementIdentLevel();
803           writer += "return o";
804         },
805         options.gen_jvmstatic);
806   }
807 
808   // Generate a method to create a vector from a Kotlin array.
GenerateCreateVectorField(FieldDef & field,CodeWriter & writer,const IDLOptions options) const809   void GenerateCreateVectorField(FieldDef &field, CodeWriter &writer,
810                                  const IDLOptions options) const {
811     auto vector_type = field.value.type.VectorType();
812     auto method_name = namer_.Method("create", field, "vector");
813     auto array_param = GenerateKotlinOffsetArray(vector_type);
814     auto params = "builder: FlatBufferBuilder, vector:" + array_param;
815     auto return_type = GenType(field.value.type);
816     writer.SetValue("size", NumToString(InlineSize(vector_type)));
817     writer.SetValue("align", NumToString(InlineAlignment(vector_type)));
818     writer.SetValue("root", GenMethod(vector_type));
819 
820     GenerateFun(
821         writer, method_name, params, return_type,
822         [&]() {
823           writer += "builder.startVector({{size}}, vector.size, {{align}})";
824           writer += "for (i in vector.size - 1 downTo 0) {";
825           writer.IncrementIdentLevel();
826           writer += "builder.add{{root}}(vector[i])";
827           writer.DecrementIdentLevel();
828           writer += "}";
829           writer += "return builder.endVector()";
830         },
831         options.gen_jvmstatic);
832   }
833 
GenerateStartVectorField(FieldDef & field,CodeWriter & writer,const IDLOptions options) const834   void GenerateStartVectorField(FieldDef &field, CodeWriter &writer,
835                                 const IDLOptions options) const {
836     // Generate a method to start a vector, data to be added manually
837     // after.
838     auto vector_type = field.value.type.VectorType();
839     auto params = "builder: FlatBufferBuilder, numElems: Int";
840     writer.SetValue("size", NumToString(InlineSize(vector_type)));
841     writer.SetValue("align", NumToString(InlineAlignment(vector_type)));
842 
843     GenerateFunOneLine(
844         writer, namer_.Method("start", field, "Vector"), params, "",
845         [&]() {
846           writer += "builder.startVector({{size}}, numElems, {{align}})";
847         },
848         options.gen_jvmstatic);
849   }
850 
GenerateAddField(std::string field_pos,FieldDef & field,CodeWriter & writer,const IDLOptions options) const851   void GenerateAddField(std::string field_pos, FieldDef &field,
852                         CodeWriter &writer, const IDLOptions options) const {
853     auto field_type = GenType(field.value.type);
854     auto secondArg = namer_.Variable(field.name) + ": " + field_type;
855 
856     auto content = [&]() {
857       auto method = GenMethod(field.value.type);
858       auto default_value = GenDefaultBufferValue(field);
859       auto field_param = namer_.Field(field);
860       if (IsEnum(field.value.type) || IsStruct(field.value.type)) {
861         field_param += ".value";
862       }
863 
864       writer.SetValue("field_name", namer_.Field(field));
865       writer.SetValue("field_param", field_param);
866       writer.SetValue("method_name", method);
867       writer.SetValue("pos", field_pos);
868       writer.SetValue("default", default_value);
869 
870       if (field.key) {
871         // field has key attribute, so always need to exist
872         // even if its value is equal to default.
873         // Generated code will bypass default checking
874         // resulting in { builder.addShort(name); slot(id); }
875         writer += "builder.add{{method_name}}({{field_name}})";
876         writer += "builder.slot({{pos}})";
877       } else {
878         writer += "builder.add{{method_name}}({{pos}}, \\";
879         writer += "{{field_param}}, {{default}})";
880       }
881     };
882     auto signature = namer_.LegacyKotlinMethod("add", field, "");
883     auto params = "builder: FlatBufferBuilder, " + secondArg;
884     if (field.key) {
885       GenerateFun(writer, signature, params, "", content,
886                   options.gen_jvmstatic);
887     } else {
888       GenerateFunOneLine(writer, signature, params, "", content,
889                          options.gen_jvmstatic);
890     }
891   }
892 
893   // fun startMonster(builder: FlatBufferBuilder) = builder.startTable(11)
GenerateStartStructMethod(StructDef & struct_def,CodeWriter & code,const IDLOptions options) const894   void GenerateStartStructMethod(StructDef &struct_def, CodeWriter &code,
895                                  const IDLOptions options) const {
896     GenerateFunOneLine(
897         code, namer_.LegacyJavaMethod2("start", struct_def, ""),
898         "builder: FlatBufferBuilder", "",
899         [&]() {
900           code += "builder.startTable(" +
901                   NumToString(struct_def.fields.vec.size()) + ")";
902         },
903         options.gen_jvmstatic);
904   }
905 
GenerateTableCreator(StructDef & struct_def,CodeWriter & writer,const IDLOptions options) const906   void GenerateTableCreator(StructDef &struct_def, CodeWriter &writer,
907                             const IDLOptions options) const {
908     // Generate a method that creates a table in one go. This is only possible
909     // when the table has no struct fields, since those have to be created
910     // inline, and there's no way to do so in Java.
911     bool has_no_struct_fields = true;
912     int num_fields = 0;
913     auto fields_vec = struct_def.fields.vec;
914 
915     for (auto it = fields_vec.begin(); it != fields_vec.end(); ++it) {
916       auto &field = **it;
917       if (field.deprecated) continue;
918       if (IsStruct(field.value.type)) {
919         has_no_struct_fields = false;
920       } else {
921         num_fields++;
922       }
923     }
924     // JVM specifications restrict default constructor params to be < 255.
925     // Longs and doubles take up 2 units, so we set the limit to be < 127.
926     if (has_no_struct_fields && num_fields && num_fields < 127) {
927       // Generate a table constructor of the form:
928       // public static int createName(FlatBufferBuilder builder, args...)
929 
930       auto name = namer_.LegacyJavaMethod2("create", struct_def, "");
931       std::stringstream params;
932       params << "builder: FlatBufferBuilder";
933       for (auto it = fields_vec.begin(); it != fields_vec.end(); ++it) {
934         auto &field = **it;
935         if (field.deprecated) continue;
936         params << ", " << namer_.Variable(field);
937         if (!IsScalar(field.value.type.base_type)) {
938           params << "Offset: ";
939         } else {
940           params << ": ";
941         }
942         auto optional = field.IsScalarOptional() ? "?" : "";
943         params << GenType(field.value.type) << optional;
944       }
945 
946       GenerateFun(
947           writer, name, params.str(), "Offset<" + namer_.Type(struct_def) + '>',
948           [&]() {
949             writer.SetValue("vec_size", NumToString(fields_vec.size()));
950             writer.SetValue("end_method",
951                             namer_.Method("end", struct_def.name));
952             writer += "builder.startTable({{vec_size}})";
953 
954             auto sortbysize = struct_def.sortbysize;
955             auto largest = sortbysize ? sizeof(largest_scalar_t) : 1;
956             for (size_t size = largest; size; size /= 2) {
957               for (auto it = fields_vec.rbegin(); it != fields_vec.rend();
958                    ++it) {
959                 auto &field = **it;
960                 auto base_type_size = SizeOf(field.value.type.base_type);
961                 if (!field.deprecated &&
962                     (!sortbysize || size == base_type_size)) {
963                   writer.SetValue("field_name", namer_.Field(field));
964 
965                   // we wrap on null check for scalar optionals
966                   writer += field.IsScalarOptional()
967                                 ? "{{field_name}}?.run { \\"
968                                 : "\\";
969 
970                   writer += namer_.LegacyKotlinMethod("add", field, "") +
971                             "(builder, {{field_name}}\\";
972                   if (!IsScalar(field.value.type.base_type)) {
973                     writer += "Offset\\";
974                   }
975                   // we wrap on null check for scalar optionals
976                   writer += field.IsScalarOptional() ? ") }" : ")";
977                 }
978               }
979             }
980             writer += "return {{end_method}}(builder)";
981           },
982           options.gen_jvmstatic);
983     }
984   }
GenerateBufferHasIdentifier(StructDef & struct_def,CodeWriter & writer,IDLOptions options) const985   void GenerateBufferHasIdentifier(StructDef &struct_def, CodeWriter &writer,
986                                    IDLOptions options) const {
987     auto file_identifier = parser_.file_identifier_;
988     // Check if a buffer has the identifier.
989     if (parser_.root_struct_def_ != &struct_def || !file_identifier.length())
990       return;
991     auto name = namer_.Function(struct_def);
992     GenerateFunOneLine(
993         writer, name + "BufferHasIdentifier", "buffer: ReadWriteBuffer",
994         "Boolean",
995         [&]() {
996           writer += "hasIdentifier(buffer, \"" + file_identifier + "\")";
997         },
998         options.gen_jvmstatic);
999   }
1000 
GenerateStructGetters(StructDef & struct_def,CodeWriter & writer) const1001   void GenerateStructGetters(StructDef &struct_def, CodeWriter &writer) const {
1002     auto fields_vec = struct_def.fields.vec;
1003     FieldDef *key_field = nullptr;
1004     for (auto it = fields_vec.begin(); it != fields_vec.end(); ++it) {
1005       auto &field = **it;
1006       if (field.deprecated) continue;
1007       if (field.key) key_field = &field;
1008 
1009       GenerateComment(field.doc_comment, writer, &comment_config);
1010 
1011       auto field_name = namer_.Field(field);
1012       auto field_type = GenTypeGet(field.value.type);
1013       auto field_default_value = GenDefaultValue(field);
1014       auto return_type = GetterReturnType(field);
1015       auto bbgetter = ByteBufferGetter(field.value.type, "bb");
1016       auto offset_val = NumToString(field.value.offset);
1017       auto offset_prefix =
1018           "val o = offset(" + offset_val + "); return o != 0 ? ";
1019       auto value_base_type = field.value.type.base_type;
1020       // Most field accessors need to retrieve and test the field offset
1021       // first, this is the offset value for that:
1022       writer.SetValue("offset", NumToString(field.value.offset));
1023       writer.SetValue("return_type", return_type);
1024       writer.SetValue("field_type", field_type);
1025       writer.SetValue("field_name", field_name);
1026       writer.SetValue("field_default", field_default_value);
1027       writer.SetValue("bbgetter", bbgetter);
1028       // Generate the accessors that don't do object reuse.
1029       if (value_base_type == BASE_TYPE_STRUCT) {
1030         // Calls the accessor that takes an accessor object with a
1031         // new object.
1032         // val pos
1033         //     get() = pos(Vec3())
1034         GenerateGetterOneLine(writer, field_name, return_type, [&]() {
1035           writer += "{{field_name}}({{field_type}}())";
1036         });
1037       } else if (value_base_type == BASE_TYPE_VECTOR &&
1038                  field.value.type.element == BASE_TYPE_STRUCT) {
1039         // Accessors for vectors of structs also take accessor objects,
1040         // this generates a variant without that argument.
1041         // ex: fun weapons(j: Int) = weapons(Weapon(), j)
1042         GenerateFunOneLine(writer, field_name, "j: Int", return_type, [&]() {
1043           writer += "{{field_name}}({{field_type}}(), j)";
1044         });
1045       }
1046 
1047       if (IsScalar(value_base_type)) {
1048         if (struct_def.fixed) {
1049           GenerateGetterOneLine(writer, field_name, return_type, [&]() {
1050             std::string found = "{{bbgetter}}(bufferPos + {{offset}})";
1051             writer += WrapEnumValue(field.value.type, found);
1052           });
1053         } else {
1054           GenerateGetterOneLine(writer, field_name, return_type, [&]() {
1055             std::string found = "{{bbgetter}}(it + bufferPos)";
1056             writer += LookupFieldOneLine(offset_val,
1057                                          WrapEnumValue(field.value.type, found),
1058                                          "{{field_default}}");
1059           });
1060         }
1061       } else {
1062         switch (value_base_type) {
1063           case BASE_TYPE_STRUCT:
1064             if (struct_def.fixed) {
1065               // create getter with object reuse
1066               // ex:
1067               // fun pos(obj: Vec3) : Vec3? = obj.init(bufferPos + 4, bb)
1068               // ? adds nullability annotation
1069               GenerateFunOneLine(
1070                   writer, field_name, "obj: " + field_type, return_type, [&]() {
1071                     writer += "obj.init(bufferPos + {{offset}}, bb)";
1072                   });
1073             } else {
1074               // create getter with object reuse
1075               // ex:
1076               //  fun pos(obj: Vec3) : Vec3? {
1077               //      val o = offset(4)
1078               //      return if(o != 0) {
1079               //          obj.init(o + bufferPos, bb)
1080               //      else {
1081               //          null
1082               //      }
1083               //  }
1084               // ? adds nullability annotation
1085               GenerateFunOneLine(
1086                   writer, field_name, "obj: " + field_type, return_type, [&]() {
1087                     auto fixed = field.value.type.struct_def->fixed;
1088 
1089                     writer.SetValue("seek", Indirect("it + bufferPos", fixed));
1090                     writer += LookupFieldOneLine(
1091                         offset_val, "obj.init({{seek}}, bb)", "null");
1092                   });
1093             }
1094             break;
1095           case BASE_TYPE_STRING:
1096             // create string getter
1097             // e.g.
1098             // val Name : String?
1099             //     get() = {
1100             //         val o = offset(10)
1101             //         return if (o != 0) string(o + bufferPos) else null
1102             //     }
1103             // ? adds nullability annotation
1104             GenerateGetterOneLine(writer, field_name, return_type, [&]() {
1105               writer += LookupFieldOneLine(offset_val, "string(it + bufferPos)",
1106                                            "null");
1107             });
1108             break;
1109           case BASE_TYPE_VECTOR: {
1110             // e.g.
1111             // fun inventory(j: Int) : UByte {
1112             //     val o = offset(14)
1113             //     return if (o != 0) {
1114             //         bb.get(vector(it) + j * 1).toUByte()
1115             //     } else {
1116             //        0
1117             //     }
1118             // }
1119 
1120             auto vectortype = field.value.type.VectorType();
1121             std::string params = "j: Int";
1122 
1123             if (vectortype.base_type == BASE_TYPE_STRUCT ||
1124                 vectortype.base_type == BASE_TYPE_UNION) {
1125               params = "obj: " + field_type + ", j: Int";
1126             }
1127 
1128             GenerateFunOneLine(writer, field_name, params, return_type, [&]() {
1129               auto inline_size = NumToString(InlineSize(vectortype));
1130               auto index = "vector(it) + j * " + inline_size;
1131               std::string found = "";
1132               writer.SetValue("index", index);
1133 
1134               if (IsEnum(vectortype)) {
1135                 found = "{{field_type}}({{bbgetter}}({{index}}))";
1136               } else {
1137                 switch (vectortype.base_type) {
1138                   case BASE_TYPE_STRUCT: {
1139                     bool fixed = vectortype.struct_def->fixed;
1140                     writer.SetValue("index", Indirect(index, fixed));
1141                     found = "obj.init({{index}}, bb)";
1142                     break;
1143                   }
1144                   case BASE_TYPE_UNION:
1145                     found = "{{bbgetter}}(obj, {{index}})";
1146                     break;
1147                   case BASE_TYPE_UTYPE:
1148                     found = "{{field_type}}({{bbgetter}}({{index}}))";
1149                     break;
1150                   default: found = "{{bbgetter}}({{index}})";
1151                 }
1152               }
1153               writer +=
1154                   LookupFieldOneLine(offset_val, found, "{{field_default}}");
1155             });
1156             break;
1157           }
1158           case BASE_TYPE_UNION:
1159             GenerateFunOneLine(
1160                 writer, field_name, "obj: " + field_type, return_type, [&]() {
1161                   writer += LookupFieldOneLine(
1162                       offset_val, bbgetter + "(obj, it + bufferPos)", "null");
1163                 });
1164             break;
1165           default: FLATBUFFERS_ASSERT(0);
1166         }
1167       }
1168 
1169       if (value_base_type == BASE_TYPE_VECTOR) {
1170         // Generate Lenght functions for vectors
1171         GenerateGetterOneLine(writer, field_name + "Length", "Int", [&]() {
1172           writer += LookupFieldOneLine(offset_val, "vectorLength(it)", "0");
1173         });
1174 
1175         // See if we should generate a by-key accessor.
1176         if (field.value.type.element == BASE_TYPE_STRUCT &&
1177             !field.value.type.struct_def->fixed) {
1178           auto &sd = *field.value.type.struct_def;
1179           auto &fields = sd.fields.vec;
1180           for (auto kit = fields.begin(); kit != fields.end(); ++kit) {
1181             auto &kfield = **kit;
1182             if (kfield.key) {
1183               auto qualified_name = TypeInNameSpace(sd);
1184               auto name = namer_.Method(field, "ByKey");
1185               auto params = "key: " + GenTypeGet(kfield.value.type);
1186               auto rtype = qualified_name + "?";
1187               GenerateFunOneLine(writer, name, params, rtype, [&]() {
1188                 writer += LookupFieldOneLine(
1189                     offset_val,
1190                     qualified_name + ".lookupByKey(null, vector(it), key, bb)",
1191                     "null");
1192               });
1193 
1194               auto param2 = "obj: " + qualified_name +
1195                             ", key: " + GenTypeGet(kfield.value.type);
1196               GenerateFunOneLine(writer, name, param2, rtype, [&]() {
1197                 writer += LookupFieldOneLine(
1198                     offset_val,
1199                     qualified_name + ".lookupByKey(obj, vector(it), key, bb)",
1200                     "null");
1201               });
1202 
1203               break;
1204             }
1205           }
1206         }
1207       }
1208 
1209       if ((value_base_type == BASE_TYPE_VECTOR &&
1210            IsScalar(field.value.type.VectorType().base_type)) ||
1211           value_base_type == BASE_TYPE_STRING) {
1212         auto end_idx =
1213             NumToString(value_base_type == BASE_TYPE_STRING
1214                             ? 1
1215                             : InlineSize(field.value.type.VectorType()));
1216 
1217         // Generate a ByteBuffer accessor for strings & vectors of scalars.
1218         // e.g.
1219         // fun inventoryInByteBuffer(buffer: Bytebuffer):
1220         //     ByteBuffer = vectorAsBuffer(buffer, 14, 1)
1221         GenerateFunOneLine(
1222             writer, field_name + "AsBuffer", "", "ReadBuffer", [&]() {
1223               writer.SetValue("end", end_idx);
1224               writer += "vectorAsBuffer(bb, {{offset}}, {{end}})";
1225             });
1226       }
1227 
1228       // generate object accessors if is nested_flatbuffer
1229       // fun testnestedflatbufferAsMonster() : Monster?
1230       //{ return testnestedflatbufferAsMonster(new Monster()); }
1231 
1232       if (field.nested_flatbuffer) {
1233         auto nested_type_name = TypeInNameSpace(*field.nested_flatbuffer);
1234         auto nested_method_name =
1235             field_name + "As" + field.nested_flatbuffer->name;
1236 
1237         GenerateGetterOneLine(
1238             writer, nested_method_name, nested_type_name + "?", [&]() {
1239               writer += nested_method_name + "(" + nested_type_name + "())";
1240             });
1241 
1242         GenerateFunOneLine(
1243             writer, nested_method_name, "obj: " + nested_type_name,
1244             nested_type_name + "?", [&]() {
1245               writer += LookupFieldOneLine(
1246                   offset_val, "obj.init(indirect(vector(it)), bb)", "null");
1247             });
1248       }
1249 
1250       writer += "";  // Initial line break between fields
1251     }
1252     if (struct_def.has_key && !struct_def.fixed) {
1253       // Key Comparison method
1254       GenerateOverrideFun(
1255           writer, "keysCompare",
1256           "o1: Offset<*>, o2: Offset<*>, buffer: ReadWriteBuffer", "Int",
1257           [&]() {
1258             if (IsString(key_field->value.type)) {
1259               writer.SetValue("offset", NumToString(key_field->value.offset));
1260               writer +=
1261                   " return compareStrings(offset({{offset}}, o1, "
1262                   "buffer), offset({{offset}}, o2, buffer), buffer)";
1263 
1264             } else {
1265               auto getter1 = GenLookupByKey(key_field, "buffer", "o1");
1266               auto getter2 = GenLookupByKey(key_field, "buffer", "o2");
1267               writer += "val a = " + getter1;
1268               writer += "val b = " + getter2;
1269               writer += "return (a - b).toInt().sign()";
1270             }
1271           });
1272     }
1273   }
1274 
LiteralSuffix(const Type & type)1275   static std::string LiteralSuffix(const Type &type) {
1276     auto base = IsVector(type) ? type.element : type.base_type;
1277     switch (base) {
1278       case BASE_TYPE_UINT:
1279       case BASE_TYPE_UCHAR:
1280       case BASE_TYPE_UTYPE:
1281       case BASE_TYPE_USHORT: return "u";
1282       case BASE_TYPE_ULONG: return "UL";
1283       case BASE_TYPE_LONG: return "L";
1284       default: return "";
1285     }
1286   }
1287 
WrapEnumValue(const Type & type,const std::string value) const1288   std::string WrapEnumValue(const Type &type, const std::string value) const {
1289     if (IsEnum(type)) { return GenType(type) + "(" + value + ")"; }
1290     if (IsVector(type) && IsEnum(type.VectorType())) {
1291       return GenType(type.VectorType()) + "(" + value + ")";
1292     }
1293     return value;
1294   }
1295 
GenerateCompanionObject(CodeWriter & code,const std::function<void ()> & callback) const1296   void GenerateCompanionObject(CodeWriter &code,
1297                                const std::function<void()> &callback) const {
1298     code += "companion object {";
1299     code.IncrementIdentLevel();
1300     callback();
1301     code.DecrementIdentLevel();
1302     code += "}";
1303   }
1304 
1305   // Generate a documentation comment, if available.
GenerateComment(const std::vector<std::string> & dc,CodeWriter & writer,const CommentConfig * config) const1306   void GenerateComment(const std::vector<std::string> &dc, CodeWriter &writer,
1307                        const CommentConfig *config) const {
1308     if (dc.begin() == dc.end()) {
1309       // Don't output empty comment blocks with 0 lines of comment content.
1310       return;
1311     }
1312 
1313     if (config != nullptr && config->first_line != nullptr) {
1314       writer += std::string(config->first_line);
1315     }
1316     std::string line_prefix =
1317         ((config != nullptr && config->content_line_prefix != nullptr)
1318              ? config->content_line_prefix
1319              : "///");
1320     for (auto it = dc.begin(); it != dc.end(); ++it) {
1321       writer += line_prefix + *it;
1322     }
1323     if (config != nullptr && config->last_line != nullptr) {
1324       writer += std::string(config->last_line);
1325     }
1326   }
1327 
GenerateGetRootAsAccessors(const std::string & struct_name,CodeWriter & writer,IDLOptions options) const1328   void GenerateGetRootAsAccessors(const std::string &struct_name,
1329                                   CodeWriter &writer,
1330                                   IDLOptions options) const {
1331     // Generate a special accessor for the table that when used as the root
1332     // ex: fun getRootAsMonster(buffer: ByteBuffer): Monster {...}
1333     writer.SetValue("gr_name", struct_name);
1334 
1335     // create convenience method that doesn't require an existing object
1336     GenerateJvmStaticAnnotation(writer, options.gen_jvmstatic);
1337     GenerateFunOneLine(writer, "asRoot", "buffer: ReadWriteBuffer", struct_name,
1338                        [&]() { writer += "asRoot(buffer, {{gr_name}}())"; });
1339 
1340     // create method that allows object reuse
1341     // ex: fun Monster getRootAsMonster(buffer: ByteBuffer, obj: Monster) {...}
1342     GenerateJvmStaticAnnotation(writer, options.gen_jvmstatic);
1343     GenerateFunOneLine(
1344         writer, "asRoot", "buffer: ReadWriteBuffer, obj: {{gr_name}}",
1345         struct_name, [&]() {
1346           writer +=
1347               "obj.init(buffer.getInt(buffer.limit) + buffer.limit, buffer)";
1348         });
1349   }
1350 
GenerateStaticConstructor(const StructDef & struct_def,CodeWriter & code,const IDLOptions options) const1351   void GenerateStaticConstructor(const StructDef &struct_def, CodeWriter &code,
1352                                  const IDLOptions options) const {
1353     // create a struct constructor function
1354     auto params = StructConstructorParams(struct_def);
1355     GenerateFun(
1356         code, namer_.LegacyJavaMethod2("create", struct_def, ""), params,
1357         "Offset<" + namer_.Type(struct_def) + '>',
1358         [&]() {
1359           GenStructBody(struct_def, code, "");
1360           code += "return Offset(builder.offset())";
1361         },
1362         options.gen_jvmstatic);
1363   }
1364 
StructConstructorParams(const StructDef & struct_def,const std::string & prefix="") const1365   std::string StructConstructorParams(const StructDef &struct_def,
1366                                       const std::string &prefix = "") const {
1367     // builder: FlatBufferBuilder
1368     std::stringstream out;
1369     auto field_vec = struct_def.fields.vec;
1370     if (prefix.empty()) { out << "builder: FlatBufferBuilder"; }
1371     for (auto it = field_vec.begin(); it != field_vec.end(); ++it) {
1372       auto &field = **it;
1373       if (IsStruct(field.value.type)) {
1374         // Generate arguments for a struct inside a struct. To ensure
1375         // names don't clash, and to make it obvious these arguments are
1376         // constructing a nested struct, prefix the name with the field
1377         // name.
1378         out << StructConstructorParams(*field.value.type.struct_def,
1379                                        prefix + (namer_.Variable(field) + "_"));
1380       } else {
1381         out << ", " << prefix << namer_.Variable(field) << ": "
1382             << GenType(field.value.type);
1383       }
1384     }
1385     return out.str();
1386   }
1387 
GenerateVarGetterSetterOneLine(CodeWriter & writer,const std::string & name,const std::string & type,const std::string & getter,const std::string & setter)1388   static void GenerateVarGetterSetterOneLine(CodeWriter &writer,
1389                                              const std::string &name,
1390                                              const std::string &type,
1391                                              const std::string &getter,
1392                                              const std::string &setter) {
1393     // Generates Kotlin getter for properties
1394     // e.g.:
1395     // val prop: Mytype
1396     //     get() = {
1397     //       return x
1398     //     }
1399     writer.SetValue("name", name);
1400     writer.SetValue("type", type);
1401     writer += "var {{name}} : {{type}}";
1402     writer.IncrementIdentLevel();
1403     writer += "get() = " + getter;
1404     writer += "set(value) = " + setter;
1405     writer.DecrementIdentLevel();
1406   }
1407 
GeneratePropertyOneLine(CodeWriter & writer,const std::string & name,const std::string & type,const std::function<void ()> & body)1408   static void GeneratePropertyOneLine(CodeWriter &writer,
1409                                       const std::string &name,
1410                                       const std::string &type,
1411                                       const std::function<void()> &body) {
1412     // Generates Kotlin getter for properties
1413     // e.g.:
1414     // val prop: Mytype = x
1415     writer.SetValue("_name", name);
1416     writer.SetValue("_type", type);
1417     writer += "val {{_name}} : {{_type}} = \\";
1418     body();
1419   }
GenerateGetterOneLine(CodeWriter & writer,const std::string & name,const std::string & type,const std::function<void ()> & body)1420   static void GenerateGetterOneLine(CodeWriter &writer, const std::string &name,
1421                                     const std::string &type,
1422                                     const std::function<void()> &body) {
1423     // Generates Kotlin getter for properties
1424     // e.g.:
1425     // val prop: Mytype get() = x
1426     writer.SetValue("_name", name);
1427     writer.SetValue("_type", type);
1428     writer += "val {{_name}} : {{_type}} get() = \\";
1429     body();
1430   }
1431 
GenerateGetter(CodeWriter & writer,const std::string & name,const std::string & type,const std::function<void ()> & body)1432   static void GenerateGetter(CodeWriter &writer, const std::string &name,
1433                              const std::string &type,
1434                              const std::function<void()> &body) {
1435     // Generates Kotlin getter for properties
1436     // e.g.:
1437     // val prop: Mytype
1438     //     get() = {
1439     //       return x
1440     //     }
1441     writer.SetValue("name", name);
1442     writer.SetValue("type", type);
1443     writer += "val {{name}} : {{type}}";
1444     writer.IncrementIdentLevel();
1445     writer += "get() {";
1446     writer.IncrementIdentLevel();
1447     body();
1448     writer.DecrementIdentLevel();
1449     writer += "}";
1450     writer.DecrementIdentLevel();
1451   }
1452 
GenerateFun(CodeWriter & writer,const std::string & name,const std::string & params,const std::string & returnType,const std::function<void ()> & body,bool gen_jvmstatic=false)1453   static void GenerateFun(CodeWriter &writer, const std::string &name,
1454                           const std::string &params,
1455                           const std::string &returnType,
1456                           const std::function<void()> &body,
1457                           bool gen_jvmstatic = false) {
1458     // Generates Kotlin function
1459     // e.g.:
1460     // fun path(j: Int): Vec3 {
1461     //     return path(Vec3(), j)
1462     // }
1463     auto noreturn = returnType.empty();
1464     writer.SetValue("name", name);
1465     writer.SetValue("params", params);
1466     writer.SetValue("return_type", noreturn ? "" : ": " + returnType);
1467     GenerateJvmStaticAnnotation(writer, gen_jvmstatic);
1468     writer += "fun {{name}}({{params}}) {{return_type}} {";
1469     writer.IncrementIdentLevel();
1470     body();
1471     writer.DecrementIdentLevel();
1472     writer += "}";
1473   }
1474 
GenerateFunOneLine(CodeWriter & writer,const std::string & name,const std::string & params,const std::string & returnType,const std::function<void ()> & body,bool gen_jvmstatic=false)1475   static void GenerateFunOneLine(CodeWriter &writer, const std::string &name,
1476                                  const std::string &params,
1477                                  const std::string &returnType,
1478                                  const std::function<void()> &body,
1479                                  bool gen_jvmstatic = false) {
1480     // Generates Kotlin function
1481     // e.g.:
1482     // fun path(j: Int): Vec3 = return path(Vec3(), j)
1483     auto ret = returnType.empty() ? "" : " : " + returnType;
1484     GenerateJvmStaticAnnotation(writer, gen_jvmstatic);
1485     writer += "fun " + name + "(" + params + ")" + ret + " = \\";
1486     body();
1487   }
1488 
GenerateOverrideFun(CodeWriter & writer,const std::string & name,const std::string & params,const std::string & returnType,const std::function<void ()> & body)1489   static void GenerateOverrideFun(CodeWriter &writer, const std::string &name,
1490                                   const std::string &params,
1491                                   const std::string &returnType,
1492                                   const std::function<void()> &body) {
1493     // Generates Kotlin function
1494     // e.g.:
1495     // override fun path(j: Int): Vec3 = return path(Vec3(), j)
1496     writer += "override \\";
1497     GenerateFun(writer, name, params, returnType, body);
1498   }
1499 
GenerateOverrideFunOneLine(CodeWriter & writer,const std::string & name,const std::string & params,const std::string & returnType,const std::string & statement)1500   static void GenerateOverrideFunOneLine(CodeWriter &writer,
1501                                          const std::string &name,
1502                                          const std::string &params,
1503                                          const std::string &returnType,
1504                                          const std::string &statement) {
1505     // Generates Kotlin function
1506     // e.g.:
1507     // override fun path(j: Int): Vec3 = return path(Vec3(), j)
1508     writer.SetValue("name", name);
1509     writer.SetValue("params", params);
1510     writer.SetValue("return_type",
1511                     returnType.empty() ? "" : " : " + returnType);
1512     writer += "override fun {{name}}({{params}}){{return_type}} = \\";
1513     writer += statement;
1514   }
1515 
LookupFieldOneLine(const std::string & offset,const std::string & found,const std::string & not_found)1516   static std::string LookupFieldOneLine(const std::string &offset,
1517                                         const std::string &found,
1518                                         const std::string &not_found) {
1519     return "lookupField(" + offset + ", " + not_found + " ) { " + found + " }";
1520   }
1521 
Indirect(const std::string & index,bool fixed)1522   static std::string Indirect(const std::string &index, bool fixed) {
1523     // We apply indirect() and struct is not fixed.
1524     if (!fixed) return "indirect(" + index + ")";
1525     return index;
1526   }
1527 
NotFoundReturn(BaseType el)1528   static std::string NotFoundReturn(BaseType el) {
1529     switch (el) {
1530       case BASE_TYPE_FLOAT: return "0.0f";
1531       case BASE_TYPE_DOUBLE: return "0.0";
1532       case BASE_TYPE_BOOL: return "false";
1533       case BASE_TYPE_LONG:
1534       case BASE_TYPE_INT:
1535       case BASE_TYPE_CHAR:
1536       case BASE_TYPE_SHORT: return "0";
1537       case BASE_TYPE_UINT:
1538       case BASE_TYPE_UCHAR:
1539       case BASE_TYPE_USHORT:
1540       case BASE_TYPE_UTYPE: return "0u";
1541       case BASE_TYPE_ULONG: return "0uL";
1542       default: return "null";
1543     }
1544   }
1545 
1546   // Prepend @JvmStatic to methods in companion object.
GenerateJvmStaticAnnotation(CodeWriter & code,bool gen_jvmstatic)1547   static void GenerateJvmStaticAnnotation(CodeWriter &code,
1548                                           bool gen_jvmstatic) {
1549     if (gen_jvmstatic) { code += "@JvmStatic"; }
1550   }
1551 
1552   const IdlNamer namer_;
1553 };
1554 }  // namespace kotlin
1555 
GenerateKotlinKMP(const Parser & parser,const std::string & path,const std::string & file_name)1556 static bool GenerateKotlinKMP(const Parser &parser, const std::string &path,
1557                        const std::string &file_name) {
1558   kotlin::KotlinKMPGenerator generator(parser, path, file_name);
1559   return generator.generate();
1560 }
1561 
1562 namespace {
1563 
1564 class KotlinKMPCodeGenerator : public CodeGenerator {
1565  public:
GenerateCode(const Parser & parser,const std::string & path,const std::string & filename)1566   Status GenerateCode(const Parser &parser, const std::string &path,
1567                       const std::string &filename) override {
1568     if (!GenerateKotlinKMP(parser, path, filename)) { return Status::ERROR; }
1569     return Status::OK;
1570   }
1571 
GenerateCode(const uint8_t *,int64_t,const CodeGenOptions &)1572   Status GenerateCode(const uint8_t *, int64_t,
1573                       const CodeGenOptions &) override {
1574     return Status::NOT_IMPLEMENTED;
1575   }
1576 
GenerateMakeRule(const Parser & parser,const std::string & path,const std::string & filename,std::string & output)1577   Status GenerateMakeRule(const Parser &parser, const std::string &path,
1578                           const std::string &filename,
1579                           std::string &output) override {
1580     (void)parser;
1581     (void)path;
1582     (void)filename;
1583     (void)output;
1584     return Status::NOT_IMPLEMENTED;
1585   }
1586 
GenerateGrpcCode(const Parser & parser,const std::string & path,const std::string & filename)1587   Status GenerateGrpcCode(const Parser &parser, const std::string &path,
1588                           const std::string &filename) override {
1589     (void)parser;
1590     (void)path;
1591     (void)filename;
1592     return Status::NOT_IMPLEMENTED;
1593   }
1594 
GenerateRootFile(const Parser & parser,const std::string & path)1595   Status GenerateRootFile(const Parser &parser,
1596                           const std::string &path) override {
1597     (void)parser;
1598     (void)path;
1599     return Status::NOT_IMPLEMENTED;
1600   }
IsSchemaOnly() const1601   bool IsSchemaOnly() const override { return true; }
1602 
SupportsBfbsGeneration() const1603   bool SupportsBfbsGeneration() const override { return false; }
1604 
SupportsRootFileGeneration() const1605   bool SupportsRootFileGeneration() const override { return false; }
1606 
Language() const1607   IDLOptions::Language Language() const override {
1608     return IDLOptions::kKotlinKmp;
1609   }
1610 
LanguageName() const1611   std::string LanguageName() const override { return "Kotlin"; }
1612 };
1613 }  // namespace
1614 
NewKotlinKMPCodeGenerator()1615 std::unique_ptr<CodeGenerator> NewKotlinKMPCodeGenerator() {
1616   return std::unique_ptr<KotlinKMPCodeGenerator>(new KotlinKMPCodeGenerator());
1617 }
1618 
1619 }  // namespace flatbuffers
1620