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