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