blob: ed85b2cbade3cd7dab8842bd41b34c8989b44d3a [file] [log] [blame]
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001/*
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>
Austin Schuh272c6132020-11-14 16:37:52 -080021
Austin Schuhe89fa2d2019-08-14 20:24:23 -070022#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)
Austin Schuh272c6132020-11-14 16:37:52 -080027# include <cctype>
Austin Schuhe89fa2d2019-08-14 20:24:23 -070028#endif // defined(FLATBUFFERS_CPP98_STL)
29
30namespace flatbuffers {
31
32namespace kotlin {
33
34typedef std::map<std::string, std::pair<std::string, std::string> > FbbParamMap;
35static TypedFloatConstantGenerator KotlinFloatGen("Double.", "Float.", "NaN",
36 "POSITIVE_INFINITY",
37 "NEGATIVE_INFINITY");
38
Austin Schuh272c6132020-11-14 16:37:52 -080039static const CommentConfig comment_config = { "/**", " *", " */" };
Austin Schuhe89fa2d2019-08-14 20:24:23 -070040static const std::string ident_pad = " ";
41static const char *keywords[] = {
Austin Schuh272c6132020-11-14 16:37:52 -080042 "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};
Austin Schuhe89fa2d2019-08-14 20:24:23 -070048
49// Escape Keywords
50static std::string Esc(const std::string &name) {
51 for (size_t i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) {
Austin Schuh272c6132020-11-14 16:37:52 -080052 if (name == keywords[i]) { return MakeCamel(name + "_", false); }
Austin Schuhe89fa2d2019-08-14 20:24:23 -070053 }
54
55 return MakeCamel(name, false);
56}
57
58class KotlinGenerator : public BaseGenerator {
59 public:
60 KotlinGenerator(const Parser &parser, const std::string &path,
61 const std::string &file_name)
Austin Schuh272c6132020-11-14 16:37:52 -080062 : BaseGenerator(parser, path, file_name, "", ".", "kt"),
Austin Schuhe89fa2d2019-08-14 20:24:23 -070063 cur_name_space_(nullptr) {}
64
65 KotlinGenerator &operator=(const KotlinGenerator &);
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;
Austin Schuh272c6132020-11-14 16:37:52 -080091 GenStruct(struct_def, structWriter, parser_.opts);
Austin Schuhe89fa2d2019-08-14 20:24:23 -070092 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.
110 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
132 const Namespace *CurrentNameSpace() const FLATBUFFERS_OVERRIDE {
133 return cur_name_space_;
134 }
135
136 static bool IsEnum(const Type &type) {
137 return type.enum_def != nullptr && IsInteger(type.base_type);
138 }
139
140 static std::string GenTypeBasic(const BaseType &type) {
141 // clang-format off
Austin Schuh272c6132020-11-14 16:37:52 -0800142 static const char * const kotlin_typename[] = {
143 #define FLATBUFFERS_TD(ENUM, IDLTYPE, \
144 CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE, ...) \
145 #KTYPE,
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700146 FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
Austin Schuh272c6132020-11-14 16:37:52 -0800147 #undef FLATBUFFERS_TD
148 };
149 // clang-format on
150 return kotlin_typename[type];
151 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700152
Austin Schuh272c6132020-11-14 16:37:52 -0800153 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";
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700159 }
Austin Schuh272c6132020-11-14 16:37:52 -0800160 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700161
Austin Schuh272c6132020-11-14 16:37:52 -0800162 // with the addition of optional scalar types,
163 // we are adding the nullable '?' operator to return type of a field.
164 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 += "?";
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700176 }
Austin Schuh272c6132020-11-14 16:37:52 -0800177 return r_type;
178 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700179
Austin Schuh272c6132020-11-14 16:37:52 -0800180 std::string GenTypeGet(const Type &type) const {
181 return IsScalar(type.base_type) ? GenTypeBasic(type.base_type)
182 : GenTypePointer(type);
183 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700184
Austin Schuh272c6132020-11-14 16:37:52 -0800185 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 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700193
Austin Schuh272c6132020-11-14 16:37:52 -0800194 // 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
199 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) {
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700206 case BASE_TYPE_DOUBLE:
Austin Schuh272c6132020-11-14 16:37:52 -0800207 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
226 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
233 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
259 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.
322 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
348 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";
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700359 case BASE_TYPE_CHAR:
360 case BASE_TYPE_UCHAR:
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700361 case BASE_TYPE_BOOL:
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700362 case BASE_TYPE_NONE:
Austin Schuh272c6132020-11-14 16:37:52 -0800363 case BASE_TYPE_UTYPE: return "bb.put";
364 default: return "bb.put" + MakeCamel(GenTypeBasic(type.base_type));
365 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700366 }
Austin Schuh272c6132020-11-14 16:37:52 -0800367 return "";
368 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700369
Austin Schuh272c6132020-11-14 16:37:52 -0800370 // Returns the function name that is able to read a value of the given type.
371 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 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700378
Austin Schuh272c6132020-11-14 16:37:52 -0800379 // Returns the method name for use with add/put calls.
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.
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 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700404 }
Austin Schuh272c6132020-11-14 16:37:52 -0800405 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700406
Austin Schuh272c6132020-11-14 16:37:52 -0800407 // Recusively generate struct construction statements of the form:
408 // builder.putType(name);
409 // and insert manual padding.
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 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700432 }
Austin Schuh272c6132020-11-14 16:37:52 -0800433 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700434
Austin Schuh272c6132020-11-14 16:37:52 -0800435 std::string GenByteBufferLength(const char *bb_name) const {
436 std::string bb_len = bb_name;
437 bb_len += ".capacity()";
438 return bb_len;
439 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700440
Austin Schuh272c6132020-11-14 16:37:52 -0800441 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
455 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_1_12_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) {
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700510 auto &field = **it;
Austin Schuh272c6132020-11-14 16:37:52 -0800511 field_pos++;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700512 if (field.deprecated) continue;
513 if (field.key) key_field = &field;
Austin Schuh272c6132020-11-14 16:37:52 -0800514 GenerateAddField(NumToString(field_pos), field, writer, options);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700515
Austin Schuh272c6132020-11-14 16:37:52 -0800516 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 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700524
Austin Schuh272c6132020-11-14 16:37:52 -0800525 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
549 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
609 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 }
621 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
634 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.required) { continue; }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700650 writer.SetValue("offset", NumToString(field.value.offset));
Austin Schuh272c6132020-11-14 16:37:52 -0800651 writer += "builder.required(o, {{offset}})";
652 }
653 writer.DecrementIdentLevel();
654 writer += "return o";
655 },
656 options.gen_jvmstatic);
657 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700658
Austin Schuh272c6132020-11-14 16:37:52 -0800659 // Generate a method to create a vector from a Kotlin array.
660 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
685 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
703 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
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
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)
763 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
774 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 }
854 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
869 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";
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700998 }
999
Austin Schuh272c6132020-11-14 16:37:52 -08001000 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;
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001012 }
1013 case BASE_TYPE_UNION:
Austin Schuh272c6132020-11-14 16:37:52 -08001014 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; });
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001021 });
Austin Schuh272c6132020-11-14 16:37:52 -08001022 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);
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001032 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001033 }
1034
Austin Schuh272c6132020-11-14 16:37:52 -08001035 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");
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001039 });
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001040
Austin Schuh272c6132020-11-14 16:37:52 -08001041 // 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;
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001076 }
Austin Schuh272c6132020-11-14 16:37:52 -08001077 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001078 }
Austin Schuh272c6132020-11-14 16:37:52 -08001079 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001080
Austin Schuh272c6132020-11-14 16:37:52 -08001081 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)
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001092
Austin Schuh272c6132020-11-14 16:37:52 -08001093 GenerateGetterOneLine(
1094 writer, field_name + "AsByteBuffer", "ByteBuffer", [&]() {
1095 writer.SetValue("end", end_idx);
1096 writer += "__vector_as_bytebuffer({{offset}}, {{end}})";
1097 });
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001098
Austin Schuh272c6132020-11-14 16:37:52 -08001099 // 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 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001110
Austin Schuh272c6132020-11-14 16:37:52 -08001111 // generate object accessors if is nested_flatbuffer
1112 // fun testnestedflatbufferAsMonster() : Monster?
1113 //{ return testnestedflatbufferAsMonster(new Monster()); }
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001114
Austin Schuh272c6132020-11-14 16:37:52 -08001115 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;
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001119
Austin Schuh272c6132020-11-14 16:37:52 -08001120 GenerateGetterOneLine(
1121 writer, nested_method_name, nested_type_name + "?", [&]() {
1122 writer += nested_method_name + "(" + nested_type_name + "())";
1123 });
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001124
Austin Schuh272c6132020-11-14 16:37:52 -08001125 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 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001136
Austin Schuh272c6132020-11-14 16:37:52 -08001137 // 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, ");
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001149
Austin Schuh272c6132020-11-14 16:37:52 -08001150 // 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);
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001156
Austin Schuh272c6132020-11-14 16:37:52 -08001157 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 }
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001187 }
Austin Schuh272c6132020-11-14 16:37:52 -08001188 }
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
1212 static std::string CastToUsigned(const FieldDef &field) {
1213 return CastToUsigned(field.value.type);
1214 }
1215
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
1228 static std::string CastToSigned(const FieldDef &field) {
1229 return CastToSigned(field.value.type);
1230 }
1231
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
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
1256 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.
1266 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;
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001271 }
1272
Austin Schuh272c6132020-11-14 16:37:52 -08001273 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
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
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
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
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 }
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
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
1398 static void GenerateFun(CodeWriter &writer, const std::string &name,
1399 const std::string &params,
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
1420 static void GenerateFunOneLine(CodeWriter &writer, const std::string &name,
1421 const std::string &params,
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
1437 static void GenerateOverrideFun(CodeWriter &writer, const std::string &name,
1438 const std::string &params,
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
1448 static void GenerateOverrideFunOneLine(CodeWriter &writer,
1449 const std::string &name,
1450 const std::string &params,
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
1464 static std::string OffsetWrapperOneLine(const std::string &offset,
1465 const std::string &found,
1466 const std::string &not_found) {
1467 return "val o = __offset(" + offset + "); return if (o != 0) " + found +
1468 " else " + not_found;
1469 }
1470
1471 static void OffsetWrapper(CodeWriter &code, const std::string &offset,
1472 const std::function<void()> &found,
1473 const std::function<void()> &not_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
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
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.
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_;
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001519};
1520} // namespace kotlin
1521
1522bool GenerateKotlin(const Parser &parser, const std::string &path,
1523 const std::string &file_name) {
Austin Schuh272c6132020-11-14 16:37:52 -08001524 kotlin::KotlinGenerator generator(parser, path, file_name);
1525 return generator.generate();
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001526}
1527} // namespace flatbuffers