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