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