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