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 ¶ms,
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 ¶ms,
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 ¶ms,
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 ¶ms,
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 ¬_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()> ¬_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