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