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