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