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