forked from BigfootDev/flatbuffers
* Attach JvmStatic annotation to each method of companion object Kotlin does not have static accessor so companion object used instead of static. It's so natural. But when use kotlin companion object methods on java it is very inconvenient. ```java GeneratedClassByFlatBuffer.Companion.someMethod() ``` If use @JvmStatic annotation it can be shorten like below. ```java GeneratedClassByFlatBuffer.someMethod() ``` * Formatting by Idea Google C++ style * Add comments - Commit for missing cla Signed-off-by: Yoon KyongSik <sam1287@gmail.com> * Add comments - Commit for missing cla Signed-off-by: Yoon KyongSik <sam1287@gmail.com> * Reset code formatting except modified lines Signed-off-by: Yoon KyongSik <sam1287@gmail.com> * Pass missing flag to validateVersion method Signed-off-by: Yoon KyongSik <sam1287@gmail.com> * Add annotations to missing method in companion object * addVector * createVector * endVector * tableCreator And also I tried add compiler option for generate annotation who don't like this operation. Signed-off-by: Yoon KyongSik <sam1287@gmail.com> * Modify unmatched option name in compiler usage Signed-off-by: Yoon KyongSik <sam1287@gmail.com> * Add missing operation for finishSizePrefixed and finishStructBuffer method. Signed-off-by: Yoon KyongSik <sam1287@gmail.com> * Add compiled example with --kotlin-gen-jvmstatic option. Signed-off-by: Yoon KyongSik <sam1287@gmail.com> * Fix Compiler.md Change option name from --gen-jvm-static-annotation to --kotlin-gen-jvmstatic Signed-off-by: Yoon KyongSik <sam1287@gmail.com> * Use IDLOptions reference instead of bool parameter. Signed-off-by: Yoon KyongSik <sam1287@gmail.com> * Change option name - kotlin_gen_jvmstatic to gen_jvmstatic Signed-off-by: Yoon KyongSik <sam1287@gmail.com> * Use IDLOptions reference instead of bool parameter and missing process @JvmStatic as suffix. Signed-off-by: Yoon KyongSik <sam1287@gmail.com> * Add code generation for --gen-jvmstatic option Signed-off-by: Yoon KyongSik <sam1287@gmail.com> * Miss typo directory for including. Signed-off-by: Yoon KyongSik <sam1287@gmail.com> * Miss typo variable suffix for including. Signed-off-by: Yoon KyongSik <sam1287@gmail.com> * Fix camel case to snake case. Signed-off-by: Yoon KyongSik <sam1287@gmail.com> * Rollback generate code for gen_jvmstatic option. Signed-off-by: Yoon KyongSik <sam1287@gmail.com> * Delete generated test files. Signed-off-by: Yoon KyongSik <sam1287@gmail.com> * add missing new line at end of file. Signed-off-by: Yoon KyongSik <sam1287@gmail.com> * Remove generated test file by command line. Signed-off-by: Yoon KyongSik <sam1287@gmail.com> Co-authored-by: sam <sam@jennifersoft.com>
1465 lines
57 KiB
C++
1465 lines
57 KiB
C++
/*
|
|
* Copyright 2014 Google Inc. All rights reserved.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
// independent from idl_parser, since this code is not needed for most clients
|
|
|
|
#include <functional>
|
|
#include <unordered_set>
|
|
|
|
#include "flatbuffers/code_generators.h"
|
|
#include "flatbuffers/flatbuffers.h"
|
|
#include "flatbuffers/idl.h"
|
|
#include "flatbuffers/util.h"
|
|
#if defined(FLATBUFFERS_CPP98_STL)
|
|
# include <cctype>
|
|
#endif // defined(FLATBUFFERS_CPP98_STL)
|
|
|
|
namespace flatbuffers {
|
|
|
|
namespace kotlin {
|
|
|
|
typedef std::map<std::string, std::pair<std::string, std::string> > FbbParamMap;
|
|
static TypedFloatConstantGenerator KotlinFloatGen("Double.", "Float.", "NaN",
|
|
"POSITIVE_INFINITY",
|
|
"NEGATIVE_INFINITY");
|
|
|
|
static const CommentConfig comment_config = { "/**", " *", " */" };
|
|
static const std::string ident_pad = " ";
|
|
static const char *keywords[] = {
|
|
"package", "as", "typealias", "class", "this", "super",
|
|
"val", "var", "fun", "for", "null", "true",
|
|
"false", "is", "in", "throw", "return", "break",
|
|
"continue", "object", "if", "try", "else", "while",
|
|
"do", "when", "interface", "typeof", "Any", "Character"
|
|
};
|
|
|
|
// Escape Keywords
|
|
static std::string Esc(const std::string &name) {
|
|
for (size_t i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) {
|
|
if (name == keywords[i]) { return MakeCamel(name + "_", false); }
|
|
}
|
|
|
|
return MakeCamel(name, false);
|
|
}
|
|
|
|
class KotlinGenerator : public BaseGenerator {
|
|
public:
|
|
KotlinGenerator(const Parser &parser, const std::string &path,
|
|
const std::string &file_name)
|
|
: BaseGenerator(parser, path, file_name, "", ".", "kt"),
|
|
cur_name_space_(nullptr) {}
|
|
|
|
KotlinGenerator &operator=(const KotlinGenerator &);
|
|
bool generate() FLATBUFFERS_OVERRIDE {
|
|
std::string one_file_code;
|
|
|
|
cur_name_space_ = parser_.current_namespace_;
|
|
for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
|
|
++it) {
|
|
CodeWriter enumWriter(ident_pad);
|
|
auto &enum_def = **it;
|
|
if (!parser_.opts.one_file) cur_name_space_ = enum_def.defined_namespace;
|
|
GenEnum(enum_def, enumWriter);
|
|
if (parser_.opts.one_file) {
|
|
one_file_code += enumWriter.ToString();
|
|
} else {
|
|
if (!SaveType(enum_def.name, *enum_def.defined_namespace,
|
|
enumWriter.ToString(), false))
|
|
return false;
|
|
}
|
|
}
|
|
|
|
for (auto it = parser_.structs_.vec.begin();
|
|
it != parser_.structs_.vec.end(); ++it) {
|
|
CodeWriter structWriter(ident_pad);
|
|
auto &struct_def = **it;
|
|
if (!parser_.opts.one_file)
|
|
cur_name_space_ = struct_def.defined_namespace;
|
|
GenStruct(struct_def, structWriter, parser_.opts);
|
|
if (parser_.opts.one_file) {
|
|
one_file_code += structWriter.ToString();
|
|
} else {
|
|
if (!SaveType(struct_def.name, *struct_def.defined_namespace,
|
|
structWriter.ToString(), true))
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (parser_.opts.one_file) {
|
|
return SaveType(file_name_, *parser_.current_namespace_, one_file_code,
|
|
true);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Save out the generated code for a single class while adding
|
|
// declaration boilerplate.
|
|
bool SaveType(const std::string &defname, const Namespace &ns,
|
|
const std::string &classcode, bool needs_includes) const {
|
|
if (!classcode.length()) return true;
|
|
|
|
std::string code =
|
|
"// " + std::string(FlatBuffersGeneratedWarning()) + "\n\n";
|
|
|
|
std::string namespace_name = FullNamespace(".", ns);
|
|
if (!namespace_name.empty()) {
|
|
code += "package " + namespace_name;
|
|
code += "\n\n";
|
|
}
|
|
if (needs_includes) {
|
|
code += "import java.nio.*\n";
|
|
code += "import kotlin.math.sign\n";
|
|
code += "import com.google.flatbuffers.*\n\n";
|
|
}
|
|
code += classcode;
|
|
auto filename = NamespaceDir(ns) + defname + ".kt";
|
|
return SaveFile(filename.c_str(), code, false);
|
|
}
|
|
|
|
const Namespace *CurrentNameSpace() const FLATBUFFERS_OVERRIDE {
|
|
return cur_name_space_;
|
|
}
|
|
|
|
static bool IsEnum(const Type &type) {
|
|
return type.enum_def != nullptr && IsInteger(type.base_type);
|
|
}
|
|
|
|
static std::string GenTypeBasic(const BaseType &type) {
|
|
// clang-format off
|
|
static const char * const kotlin_typename[] = {
|
|
#define FLATBUFFERS_TD(ENUM, IDLTYPE, \
|
|
CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE, ...) \
|
|
#KTYPE,
|
|
FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD)
|
|
#undef FLATBUFFERS_TD
|
|
};
|
|
// clang-format on
|
|
return kotlin_typename[type];
|
|
}
|
|
|
|
std::string GenTypePointer(const Type &type) const {
|
|
switch (type.base_type) {
|
|
case BASE_TYPE_STRING: return "String";
|
|
case BASE_TYPE_VECTOR: return GenTypeGet(type.VectorType());
|
|
case BASE_TYPE_STRUCT: return WrapInNameSpace(*type.struct_def);
|
|
default: return "Table";
|
|
}
|
|
}
|
|
|
|
std::string GenTypeGet(const Type &type) const {
|
|
return IsScalar(type.base_type) ? GenTypeBasic(type.base_type)
|
|
: GenTypePointer(type);
|
|
}
|
|
|
|
std::string GenEnumDefaultValue(const FieldDef &field) const {
|
|
auto &value = field.value;
|
|
FLATBUFFERS_ASSERT(value.type.enum_def);
|
|
auto &enum_def = *value.type.enum_def;
|
|
auto enum_val = enum_def.FindByValue(value.constant);
|
|
return enum_val ? (WrapInNameSpace(enum_def) + "." + enum_val->name)
|
|
: value.constant;
|
|
}
|
|
|
|
// Generate default values to compare against a default value when
|
|
// `force_defaults` is `false`.
|
|
// Main differences are:
|
|
// - Floats are upcasted to doubles
|
|
// - Unsigned are casted to signed
|
|
std::string GenFBBDefaultValue(const FieldDef &field) const {
|
|
auto out = GenDefaultValue(field, true);
|
|
// All FlatBufferBuilder default floating point values are doubles
|
|
if (field.value.type.base_type == BASE_TYPE_FLOAT) {
|
|
if (out.find("Float") != std::string::npos) {
|
|
out.replace(0, 5, "Double");
|
|
}
|
|
}
|
|
// Guarantee all values are doubles
|
|
if (out.back() == 'f') out.pop_back();
|
|
return out;
|
|
}
|
|
|
|
// FlatBufferBuilder only store signed types, so this function
|
|
// returns a cast for unsigned values
|
|
std::string GenFBBValueCast(const FieldDef &field) const {
|
|
if (IsUnsigned(field.value.type.base_type)) {
|
|
return CastToSigned(field.value.type);
|
|
}
|
|
return "";
|
|
}
|
|
|
|
std::string GenDefaultValue(const FieldDef &field,
|
|
bool force_signed = false) const {
|
|
auto &value = field.value;
|
|
auto base_type = field.value.type.base_type;
|
|
if (IsFloat(base_type)) {
|
|
auto val = KotlinFloatGen.GenFloatConstant(field);
|
|
if (base_type == BASE_TYPE_DOUBLE && val.back() == 'f') {
|
|
val.pop_back();
|
|
}
|
|
return val;
|
|
}
|
|
|
|
if (base_type == BASE_TYPE_BOOL) {
|
|
return value.constant == "0" ? "false" : "true";
|
|
}
|
|
|
|
std::string suffix = "";
|
|
|
|
if (base_type == BASE_TYPE_LONG || !force_signed) {
|
|
suffix = LiteralSuffix(base_type);
|
|
}
|
|
return value.constant + suffix;
|
|
}
|
|
|
|
void GenEnum(EnumDef &enum_def, CodeWriter &writer) const {
|
|
if (enum_def.generated) return;
|
|
|
|
GenerateComment(enum_def.doc_comment, writer, &comment_config);
|
|
|
|
writer += "@Suppress(\"unused\")";
|
|
writer += "@ExperimentalUnsignedTypes";
|
|
writer += "class " + Esc(enum_def.name) + " private constructor() {";
|
|
writer.IncrementIdentLevel();
|
|
|
|
GenerateCompanionObject(writer, [&]() {
|
|
// Write all properties
|
|
auto vals = enum_def.Vals();
|
|
for (auto it = vals.begin(); it != vals.end(); ++it) {
|
|
auto &ev = **it;
|
|
auto field_type = GenTypeBasic(enum_def.underlying_type.base_type);
|
|
auto val = enum_def.ToString(ev);
|
|
auto suffix = LiteralSuffix(enum_def.underlying_type.base_type);
|
|
writer.SetValue("name", Esc(ev.name));
|
|
writer.SetValue("type", field_type);
|
|
writer.SetValue("val", val + suffix);
|
|
GenerateComment(ev.doc_comment, writer, &comment_config);
|
|
writer += "const val {{name}}: {{type}} = {{val}}";
|
|
}
|
|
|
|
// Generate a generate string table for enum values.
|
|
// Problem is, if values are very sparse that could generate really
|
|
// big tables. Ideally in that case we generate a map lookup
|
|
// instead, but for the moment we simply don't output a table at all.
|
|
auto range = enum_def.Distance();
|
|
// Average distance between values above which we consider a table
|
|
// "too sparse". Change at will.
|
|
static const uint64_t kMaxSparseness = 5;
|
|
if (range / static_cast<uint64_t>(enum_def.size()) < kMaxSparseness) {
|
|
GeneratePropertyOneLine(writer, "names", "Array<String>", [&]() {
|
|
writer += "arrayOf(\\";
|
|
auto val = enum_def.Vals().front();
|
|
for (auto it = vals.begin(); it != vals.end(); ++it) {
|
|
auto ev = *it;
|
|
for (auto k = enum_def.Distance(val, ev); k > 1; --k)
|
|
writer += "\"\", \\";
|
|
val = ev;
|
|
writer += "\"" + (*it)->name + "\"\\";
|
|
if (it + 1 != vals.end()) { writer += ", \\"; }
|
|
}
|
|
writer += ")";
|
|
});
|
|
GenerateFunOneLine(writer, "name", "e: Int", "String", [&]() {
|
|
writer += "names[e\\";
|
|
if (enum_def.MinValue()->IsNonZero())
|
|
writer += " - " + enum_def.MinValue()->name + ".toInt()\\";
|
|
writer += "]";
|
|
}, parser_.opts.gen_jvmstatic);
|
|
}
|
|
});
|
|
writer.DecrementIdentLevel();
|
|
writer += "}";
|
|
}
|
|
|
|
// Returns the function name that is able to read a value of the given type.
|
|
std::string ByteBufferGetter(const Type &type,
|
|
std::string bb_var_name) const {
|
|
switch (type.base_type) {
|
|
case BASE_TYPE_STRING: return "__string";
|
|
case BASE_TYPE_STRUCT: return "__struct";
|
|
case BASE_TYPE_UNION: return "__union";
|
|
case BASE_TYPE_VECTOR:
|
|
return ByteBufferGetter(type.VectorType(), bb_var_name);
|
|
case BASE_TYPE_INT:
|
|
case BASE_TYPE_UINT: return bb_var_name + ".getInt";
|
|
case BASE_TYPE_SHORT:
|
|
case BASE_TYPE_USHORT: return bb_var_name + ".getShort";
|
|
case BASE_TYPE_ULONG:
|
|
case BASE_TYPE_LONG: return bb_var_name + ".getLong";
|
|
case BASE_TYPE_FLOAT: return bb_var_name + ".getFloat";
|
|
case BASE_TYPE_DOUBLE: return bb_var_name + ".getDouble";
|
|
case BASE_TYPE_CHAR:
|
|
case BASE_TYPE_UCHAR:
|
|
case BASE_TYPE_NONE:
|
|
case BASE_TYPE_UTYPE: return bb_var_name + ".get";
|
|
case BASE_TYPE_BOOL: return "0.toByte() != " + bb_var_name + ".get";
|
|
default:
|
|
return bb_var_name + ".get" + MakeCamel(GenTypeBasic(type.base_type));
|
|
}
|
|
}
|
|
|
|
std::string ByteBufferSetter(const Type &type) const {
|
|
if (IsScalar(type.base_type)) {
|
|
switch (type.base_type) {
|
|
case BASE_TYPE_INT:
|
|
case BASE_TYPE_UINT: return "bb.putInt";
|
|
case BASE_TYPE_SHORT:
|
|
case BASE_TYPE_USHORT: return "bb.putShort";
|
|
case BASE_TYPE_ULONG:
|
|
case BASE_TYPE_LONG: return "bb.putLong";
|
|
case BASE_TYPE_FLOAT: return "bb.putFloat";
|
|
case BASE_TYPE_DOUBLE: return "bb.putDouble";
|
|
case BASE_TYPE_CHAR:
|
|
case BASE_TYPE_UCHAR:
|
|
case BASE_TYPE_BOOL:
|
|
case BASE_TYPE_NONE:
|
|
case BASE_TYPE_UTYPE: return "bb.put";
|
|
default: return "bb.put" + MakeCamel(GenTypeBasic(type.base_type));
|
|
}
|
|
}
|
|
return "";
|
|
}
|
|
|
|
// Returns the function name that is able to read a value of the given type.
|
|
std::string GenLookupByKey(flatbuffers::FieldDef *key_field,
|
|
const std::string &bb_var_name,
|
|
const char *num = nullptr) const {
|
|
auto type = key_field->value.type;
|
|
return ByteBufferGetter(type, bb_var_name) + "(" +
|
|
GenOffsetGetter(key_field, num) + ")";
|
|
}
|
|
|
|
// Returns the method name for use with add/put calls.
|
|
static std::string GenMethod(const Type &type) {
|
|
return IsScalar(type.base_type) ? ToSignedType(type)
|
|
: (IsStruct(type) ? "Struct" : "Offset");
|
|
}
|
|
|
|
// Recursively generate arguments for a constructor, to deal with nested
|
|
// structs.
|
|
static void GenStructArgs(const StructDef &struct_def, CodeWriter &writer,
|
|
const char *nameprefix) {
|
|
for (auto it = struct_def.fields.vec.begin();
|
|
it != struct_def.fields.vec.end(); ++it) {
|
|
auto &field = **it;
|
|
if (IsStruct(field.value.type)) {
|
|
// Generate arguments for a struct inside a struct. To ensure
|
|
// names don't clash, and to make it obvious these arguments are
|
|
// constructing a nested struct, prefix the name with the field
|
|
// name.
|
|
GenStructArgs(*field.value.type.struct_def, writer,
|
|
(nameprefix + (field.name + "_")).c_str());
|
|
} else {
|
|
writer += std::string(", ") + nameprefix + "\\";
|
|
writer += MakeCamel(field.name) + ": \\";
|
|
writer += GenTypeBasic(field.value.type.base_type) + "\\";
|
|
}
|
|
}
|
|
}
|
|
|
|
// Recusively generate struct construction statements of the form:
|
|
// builder.putType(name);
|
|
// and insert manual padding.
|
|
static void GenStructBody(const StructDef &struct_def, CodeWriter &writer,
|
|
const char *nameprefix) {
|
|
writer.SetValue("align", NumToString(struct_def.minalign));
|
|
writer.SetValue("size", NumToString(struct_def.bytesize));
|
|
writer += "builder.prep({{align}}, {{size}})";
|
|
auto fields_vec = struct_def.fields.vec;
|
|
for (auto it = fields_vec.rbegin(); it != fields_vec.rend(); ++it) {
|
|
auto &field = **it;
|
|
|
|
if (field.padding) {
|
|
writer.SetValue("pad", NumToString(field.padding));
|
|
writer += "builder.pad({{pad}})";
|
|
}
|
|
if (IsStruct(field.value.type)) {
|
|
GenStructBody(*field.value.type.struct_def, writer,
|
|
(nameprefix + (field.name + "_")).c_str());
|
|
} else {
|
|
writer.SetValue("type", GenMethod(field.value.type));
|
|
writer.SetValue("argname", nameprefix + MakeCamel(field.name, false));
|
|
writer.SetValue("cast", CastToSigned(field.value.type));
|
|
writer += "builder.put{{type}}({{argname}}{{cast}})";
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string GenByteBufferLength(const char *bb_name) const {
|
|
std::string bb_len = bb_name;
|
|
bb_len += ".capacity()";
|
|
return bb_len;
|
|
}
|
|
|
|
std::string GenOffsetGetter(flatbuffers::FieldDef *key_field,
|
|
const char *num = nullptr) const {
|
|
std::string key_offset =
|
|
"__offset(" + NumToString(key_field->value.offset) + ", ";
|
|
if (num) {
|
|
key_offset += num;
|
|
key_offset += ", _bb)";
|
|
} else {
|
|
key_offset += GenByteBufferLength("bb");
|
|
key_offset += " - tableOffset, bb)";
|
|
}
|
|
return key_offset;
|
|
}
|
|
|
|
void GenStruct(StructDef &struct_def, CodeWriter &writer, IDLOptions options) const {
|
|
if (struct_def.generated) return;
|
|
|
|
GenerateComment(struct_def.doc_comment, writer, &comment_config);
|
|
auto fixed = struct_def.fixed;
|
|
|
|
writer.SetValue("struct_name", Esc(struct_def.name));
|
|
writer.SetValue("superclass", fixed ? "Struct" : "Table");
|
|
|
|
writer += "@Suppress(\"unused\")";
|
|
writer += "@ExperimentalUnsignedTypes";
|
|
writer += "class {{struct_name}} : {{superclass}}() {\n";
|
|
|
|
writer.IncrementIdentLevel();
|
|
|
|
{
|
|
// Generate the __init() method that sets the field in a pre-existing
|
|
// accessor object. This is to allow object reuse.
|
|
GenerateFun(writer, "__init", "_i: Int, _bb: ByteBuffer", "",
|
|
[&]() { writer += "__reset(_i, _bb)"; });
|
|
|
|
// Generate assign method
|
|
GenerateFun(writer, "__assign", "_i: Int, _bb: ByteBuffer",
|
|
Esc(struct_def.name), [&]() {
|
|
writer += "__init(_i, _bb)";
|
|
writer += "return this";
|
|
});
|
|
|
|
// Generate all getters
|
|
GenerateStructGetters(struct_def, writer);
|
|
|
|
// Generate Static Fields
|
|
GenerateCompanionObject(writer, [&]() {
|
|
if (!struct_def.fixed) {
|
|
FieldDef *key_field = nullptr;
|
|
|
|
// Generate verson check method.
|
|
// Force compile time error if not using the same version
|
|
// runtime.
|
|
GenerateFunOneLine(writer, "validateVersion", "", "", [&]() {
|
|
writer += "Constants.FLATBUFFERS_1_12_0()";
|
|
}, options.gen_jvmstatic);
|
|
|
|
GenerateGetRootAsAccessors(Esc(struct_def.name), writer, options);
|
|
GenerateBufferHasIdentifier(struct_def, writer, options);
|
|
GenerateTableCreator(struct_def, writer, options);
|
|
|
|
GenerateStartStructMethod(struct_def, writer, options);
|
|
|
|
// Static Add for fields
|
|
auto fields = struct_def.fields.vec;
|
|
int field_pos = -1;
|
|
for (auto it = fields.begin(); it != fields.end(); ++it) {
|
|
auto &field = **it;
|
|
field_pos++;
|
|
if (field.deprecated) continue;
|
|
if (field.key) key_field = &field;
|
|
GenerateAddField(NumToString(field_pos), field, writer, options);
|
|
|
|
if (field.value.type.base_type == BASE_TYPE_VECTOR) {
|
|
auto vector_type = field.value.type.VectorType();
|
|
if (!IsStruct(vector_type)) {
|
|
GenerateCreateVectorField(field, writer, options);
|
|
}
|
|
GenerateStartVectorField(field, writer, options);
|
|
}
|
|
}
|
|
|
|
GenerateEndStructMethod(struct_def, writer, options);
|
|
auto file_identifier = parser_.file_identifier_;
|
|
if (parser_.root_struct_def_ == &struct_def) {
|
|
GenerateFinishStructBuffer(struct_def, file_identifier, writer, options);
|
|
GenerateFinishSizePrefixed(struct_def, file_identifier, writer, options);
|
|
}
|
|
|
|
if (struct_def.has_key) {
|
|
GenerateLookupByKey(key_field, struct_def, writer, options);
|
|
}
|
|
} else {
|
|
GenerateStaticConstructor(struct_def, writer, options);
|
|
}
|
|
});
|
|
}
|
|
|
|
// class closing
|
|
writer.DecrementIdentLevel();
|
|
writer += "}";
|
|
}
|
|
|
|
// TODO: move key_field to reference instead of pointer
|
|
void GenerateLookupByKey(FieldDef *key_field, StructDef &struct_def,
|
|
CodeWriter &writer, const IDLOptions options) const {
|
|
std::stringstream params;
|
|
params << "obj: " << Esc(struct_def.name) << "?"
|
|
<< ", ";
|
|
params << "vectorLocation: Int, ";
|
|
params << "key: " << GenTypeGet(key_field->value.type) << ", ";
|
|
params << "bb: ByteBuffer";
|
|
|
|
auto statements = [&]() {
|
|
auto base_type = key_field->value.type.base_type;
|
|
writer.SetValue("struct_name", Esc(struct_def.name));
|
|
if (base_type == BASE_TYPE_STRING) {
|
|
writer +=
|
|
"val byteKey = key."
|
|
"toByteArray(java.nio.charset.StandardCharsets.UTF_8)";
|
|
}
|
|
writer += "var span = bb.getInt(vectorLocation - 4)";
|
|
writer += "var start = 0";
|
|
writer += "while (span != 0) {";
|
|
writer.IncrementIdentLevel();
|
|
writer += "var middle = span / 2";
|
|
writer +=
|
|
"val tableOffset = __indirect(vector"
|
|
"Location + 4 * (start + middle), bb)";
|
|
if (key_field->value.type.base_type == BASE_TYPE_STRING) {
|
|
writer += "val comp = compareStrings(\\";
|
|
writer += GenOffsetGetter(key_field) + "\\";
|
|
writer += ", byteKey, bb)";
|
|
} else {
|
|
auto cast = CastToUsigned(key_field->value.type);
|
|
auto get_val = GenLookupByKey(key_field, "bb");
|
|
writer += "val value = " + get_val + cast;
|
|
writer += "val comp = value.compareTo(key)";
|
|
}
|
|
writer += "when {";
|
|
writer.IncrementIdentLevel();
|
|
writer += "comp > 0 -> span = middle";
|
|
writer += "comp < 0 -> {";
|
|
writer.IncrementIdentLevel();
|
|
writer += "middle++";
|
|
writer += "start += middle";
|
|
writer += "span -= middle";
|
|
writer.DecrementIdentLevel();
|
|
writer += "}"; // end comp < 0
|
|
writer += "else -> {";
|
|
writer.IncrementIdentLevel();
|
|
writer += "return (obj ?: {{struct_name}}()).__assign(tableOffset, bb)";
|
|
writer.DecrementIdentLevel();
|
|
writer += "}"; // end else
|
|
writer.DecrementIdentLevel();
|
|
writer += "}"; // end when
|
|
writer.DecrementIdentLevel();
|
|
writer += "}"; // end while
|
|
writer += "return null";
|
|
};
|
|
GenerateFun(writer, "__lookup_by_key", params.str(),
|
|
Esc(struct_def.name) + "?", statements, options.gen_jvmstatic);
|
|
}
|
|
|
|
void GenerateFinishSizePrefixed(StructDef &struct_def,
|
|
const std::string &identifier,
|
|
CodeWriter &writer,
|
|
const IDLOptions options) const {
|
|
auto id = identifier.length() > 0 ? ", \"" + identifier + "\"" : "";
|
|
auto params = "builder: FlatBufferBuilder, offset: Int";
|
|
auto method_name = "finishSizePrefixed" + Esc(struct_def.name) + "Buffer";
|
|
GenerateFunOneLine(writer, method_name, params, "", [&]() {
|
|
writer += "builder.finishSizePrefixed(offset" + id + ")";
|
|
}, options.gen_jvmstatic);
|
|
}
|
|
void GenerateFinishStructBuffer(StructDef &struct_def,
|
|
const std::string &identifier,
|
|
CodeWriter &writer,
|
|
const IDLOptions options) const {
|
|
auto id = identifier.length() > 0 ? ", \"" + identifier + "\"" : "";
|
|
auto params = "builder: FlatBufferBuilder, offset: Int";
|
|
auto method_name = "finish" + Esc(struct_def.name) + "Buffer";
|
|
GenerateFunOneLine(writer, method_name, params, "",
|
|
[&]() { writer += "builder.finish(offset" + id + ")"; },
|
|
options.gen_jvmstatic);
|
|
}
|
|
|
|
void GenerateEndStructMethod(StructDef &struct_def, CodeWriter &writer,
|
|
const IDLOptions options) const {
|
|
// Generate end{{TableName}}(builder: FlatBufferBuilder) method
|
|
auto name = "end" + Esc(struct_def.name);
|
|
auto params = "builder: FlatBufferBuilder";
|
|
auto returns = "Int";
|
|
auto field_vec = struct_def.fields.vec;
|
|
|
|
GenerateFun(writer, name, params, returns, [&]() {
|
|
writer += "val o = builder.endTable()";
|
|
writer.IncrementIdentLevel();
|
|
for (auto it = field_vec.begin(); it != field_vec.end(); ++it) {
|
|
auto &field = **it;
|
|
if (field.deprecated || !field.required) { continue; }
|
|
writer.SetValue("offset", NumToString(field.value.offset));
|
|
writer += "builder.required(o, {{offset}})";
|
|
}
|
|
writer.DecrementIdentLevel();
|
|
writer += "return o";
|
|
}, options.gen_jvmstatic);
|
|
}
|
|
|
|
// Generate a method to create a vector from a Kotlin array.
|
|
void GenerateCreateVectorField(FieldDef &field, CodeWriter &writer,
|
|
const IDLOptions options) const {
|
|
auto vector_type = field.value.type.VectorType();
|
|
auto method_name = "create" + MakeCamel(Esc(field.name)) + "Vector";
|
|
auto params = "builder: FlatBufferBuilder, data: " +
|
|
GenTypeBasic(vector_type.base_type) + "Array";
|
|
writer.SetValue("size", NumToString(InlineSize(vector_type)));
|
|
writer.SetValue("align", NumToString(InlineAlignment(vector_type)));
|
|
writer.SetValue("root", GenMethod(vector_type));
|
|
writer.SetValue("cast", CastToSigned(vector_type));
|
|
|
|
GenerateFun(writer, method_name, params, "Int", [&]() {
|
|
writer += "builder.startVector({{size}}, data.size, {{align}})";
|
|
writer += "for (i in data.size - 1 downTo 0) {";
|
|
writer.IncrementIdentLevel();
|
|
writer += "builder.add{{root}}(data[i]{{cast}})";
|
|
writer.DecrementIdentLevel();
|
|
writer += "}";
|
|
writer += "return builder.endVector()";
|
|
}, options.gen_jvmstatic);
|
|
}
|
|
|
|
void GenerateStartVectorField(FieldDef &field, CodeWriter &writer,
|
|
const IDLOptions options) const {
|
|
// Generate a method to start a vector, data to be added manually
|
|
// after.
|
|
auto vector_type = field.value.type.VectorType();
|
|
auto params = "builder: FlatBufferBuilder, numElems: Int";
|
|
writer.SetValue("size", NumToString(InlineSize(vector_type)));
|
|
writer.SetValue("align", NumToString(InlineAlignment(vector_type)));
|
|
|
|
GenerateFunOneLine(
|
|
writer, "start" + MakeCamel(Esc(field.name) + "Vector", true), params,
|
|
"", [&]() {
|
|
writer += "builder.startVector({{size}}, numElems, {{align}})";
|
|
}, options.gen_jvmstatic);
|
|
}
|
|
|
|
void GenerateAddField(std::string field_pos, FieldDef &field,
|
|
CodeWriter &writer, const IDLOptions options) const {
|
|
auto field_type = GenTypeBasic(field.value.type.base_type);
|
|
auto secondArg = MakeCamel(Esc(field.name), false) + ": " + field_type;
|
|
GenerateFunOneLine(writer, "add" + MakeCamel(Esc(field.name), true),
|
|
"builder: FlatBufferBuilder, " + secondArg, "", [&]() {
|
|
auto method = GenMethod(field.value.type);
|
|
writer.SetValue("field_name",
|
|
MakeCamel(Esc(field.name), false));
|
|
writer.SetValue("method_name", method);
|
|
writer.SetValue("pos", field_pos);
|
|
writer.SetValue("default", GenFBBDefaultValue(field));
|
|
writer.SetValue("cast", GenFBBValueCast(field));
|
|
|
|
writer += "builder.add{{method_name}}({{pos}}, \\";
|
|
writer += "{{field_name}}{{cast}}, {{default}})";
|
|
}, options.gen_jvmstatic);
|
|
}
|
|
|
|
static std::string ToSignedType(const Type &type) {
|
|
switch (type.base_type) {
|
|
case BASE_TYPE_UINT: return GenTypeBasic(BASE_TYPE_INT);
|
|
case BASE_TYPE_ULONG: return GenTypeBasic(BASE_TYPE_LONG);
|
|
case BASE_TYPE_UCHAR:
|
|
case BASE_TYPE_NONE:
|
|
case BASE_TYPE_UTYPE: return GenTypeBasic(BASE_TYPE_CHAR);
|
|
case BASE_TYPE_USHORT: return GenTypeBasic(BASE_TYPE_SHORT);
|
|
case BASE_TYPE_VECTOR: return ToSignedType(type.VectorType());
|
|
default: return GenTypeBasic(type.base_type);
|
|
}
|
|
}
|
|
|
|
static std::string FlexBufferBuilderCast(const std::string &method,
|
|
FieldDef &field, bool isFirst) {
|
|
auto field_type = GenTypeBasic(field.value.type.base_type);
|
|
std::string to_type;
|
|
if (method == "Boolean")
|
|
to_type = "Boolean";
|
|
else if (method == "Long")
|
|
to_type = "Long";
|
|
else if (method == "Int" || method == "Offset" || method == "Struct")
|
|
to_type = "Int";
|
|
else if (method == "Byte" || method.empty())
|
|
to_type = isFirst ? "Byte" : "Int";
|
|
else if (method == "Short")
|
|
to_type = isFirst ? "Short" : "Int";
|
|
else if (method == "Double")
|
|
to_type = "Double";
|
|
else if (method == "Float")
|
|
to_type = isFirst ? "Float" : "Double";
|
|
else if (method == "UByte")
|
|
|
|
if (field_type != to_type) return ".to" + to_type + "()";
|
|
return "";
|
|
}
|
|
|
|
// fun startMonster(builder: FlatBufferBuilder) = builder.startTable(11)
|
|
void GenerateStartStructMethod(StructDef &struct_def, CodeWriter &code,
|
|
const IDLOptions options) const {
|
|
GenerateFunOneLine(code, "start" + Esc(struct_def.name),
|
|
"builder: FlatBufferBuilder", "", [&]() {
|
|
code += "builder.startTable(" +
|
|
NumToString(struct_def.fields.vec.size()) +
|
|
")";
|
|
}, options.gen_jvmstatic);
|
|
}
|
|
|
|
void GenerateTableCreator(StructDef &struct_def, CodeWriter &writer,
|
|
const IDLOptions options) const {
|
|
// Generate a method that creates a table in one go. This is only possible
|
|
// when the table has no struct fields, since those have to be created
|
|
// inline, and there's no way to do so in Java.
|
|
bool has_no_struct_fields = true;
|
|
int num_fields = 0;
|
|
auto fields_vec = struct_def.fields.vec;
|
|
|
|
for (auto it = fields_vec.begin(); it != fields_vec.end(); ++it) {
|
|
auto &field = **it;
|
|
if (field.deprecated) continue;
|
|
if (IsStruct(field.value.type)) {
|
|
has_no_struct_fields = false;
|
|
} else {
|
|
num_fields++;
|
|
}
|
|
}
|
|
// JVM specifications restrict default constructor params to be < 255.
|
|
// Longs and doubles take up 2 units, so we set the limit to be < 127.
|
|
if (has_no_struct_fields && num_fields && num_fields < 127) {
|
|
// Generate a table constructor of the form:
|
|
// public static int createName(FlatBufferBuilder builder, args...)
|
|
|
|
auto name = "create" + Esc(struct_def.name);
|
|
std::stringstream params;
|
|
params << "builder: FlatBufferBuilder";
|
|
for (auto it = fields_vec.begin(); it != fields_vec.end(); ++it) {
|
|
auto &field = **it;
|
|
if (field.deprecated) continue;
|
|
params << ", " << MakeCamel(Esc(field.name), false);
|
|
if (!IsScalar(field.value.type.base_type)) {
|
|
params << "Offset: ";
|
|
} else {
|
|
params << ": ";
|
|
}
|
|
params << GenTypeBasic(field.value.type.base_type);
|
|
}
|
|
|
|
GenerateFun(writer, name, params.str(), "Int", [&]() {
|
|
writer.SetValue("vec_size", NumToString(fields_vec.size()));
|
|
|
|
writer += "builder.startTable({{vec_size}})";
|
|
|
|
auto sortbysize = struct_def.sortbysize;
|
|
auto largest = sortbysize ? sizeof(largest_scalar_t) : 1;
|
|
for (size_t size = largest; size; size /= 2) {
|
|
for (auto it = fields_vec.rbegin(); it != fields_vec.rend(); ++it) {
|
|
auto &field = **it;
|
|
auto base_type_size = SizeOf(field.value.type.base_type);
|
|
if (!field.deprecated && (!sortbysize || size == base_type_size)) {
|
|
writer.SetValue("camel_field_name",
|
|
MakeCamel(Esc(field.name), true));
|
|
writer.SetValue("field_name", MakeCamel(Esc(field.name), false));
|
|
|
|
writer += "add{{camel_field_name}}(builder, {{field_name}}\\";
|
|
if (!IsScalar(field.value.type.base_type)) {
|
|
writer += "Offset\\";
|
|
}
|
|
writer += ")";
|
|
}
|
|
}
|
|
}
|
|
writer += "return end{{struct_name}}(builder)";
|
|
}, options.gen_jvmstatic);
|
|
}
|
|
}
|
|
void GenerateBufferHasIdentifier(StructDef &struct_def,
|
|
CodeWriter &writer, IDLOptions options) const {
|
|
auto file_identifier = parser_.file_identifier_;
|
|
// Check if a buffer has the identifier.
|
|
if (parser_.root_struct_def_ != &struct_def || !file_identifier.length())
|
|
return;
|
|
auto name = MakeCamel(Esc(struct_def.name), false);
|
|
GenerateFunOneLine(writer, name + "BufferHasIdentifier", "_bb: ByteBuffer",
|
|
"Boolean", [&]() {
|
|
writer += "__has_identifier(_bb, \"" +
|
|
file_identifier + "\")";
|
|
}, options.gen_jvmstatic);
|
|
}
|
|
|
|
void GenerateStructGetters(StructDef &struct_def, CodeWriter &writer) const {
|
|
auto fields_vec = struct_def.fields.vec;
|
|
FieldDef *key_field = nullptr;
|
|
for (auto it = fields_vec.begin(); it != fields_vec.end(); ++it) {
|
|
auto &field = **it;
|
|
if (field.deprecated) continue;
|
|
if (field.key) key_field = &field;
|
|
|
|
GenerateComment(field.doc_comment, writer, &comment_config);
|
|
|
|
auto field_name = MakeCamel(Esc(field.name), false);
|
|
auto field_type = GenTypeGet(field.value.type);
|
|
auto field_default_value = GenDefaultValue(field);
|
|
auto return_type = GenTypeGet(field.value.type);
|
|
auto bbgetter = ByteBufferGetter(field.value.type, "bb");
|
|
auto ucast = CastToUsigned(field);
|
|
auto offset_val = NumToString(field.value.offset);
|
|
auto offset_prefix =
|
|
"val o = __offset(" + offset_val + "); return o != 0 ? ";
|
|
auto value_base_type = field.value.type.base_type;
|
|
// Most field accessors need to retrieve and test the field offset
|
|
// first, this is the offset value for that:
|
|
writer.SetValue("offset", NumToString(field.value.offset));
|
|
writer.SetValue("return_type", return_type);
|
|
writer.SetValue("field_type", field_type);
|
|
writer.SetValue("field_name", field_name);
|
|
writer.SetValue("field_default", field_default_value);
|
|
writer.SetValue("bbgetter", bbgetter);
|
|
writer.SetValue("ucast", ucast);
|
|
|
|
auto opt_ret_type = return_type + "?";
|
|
// Generate the accessors that don't do object reuse.
|
|
if (value_base_type == BASE_TYPE_STRUCT) {
|
|
// Calls the accessor that takes an accessor object with a
|
|
// new object.
|
|
// val pos
|
|
// get() = pos(Vec3())
|
|
GenerateGetterOneLine(writer, field_name, opt_ret_type, [&]() {
|
|
writer += "{{field_name}}({{field_type}}())";
|
|
});
|
|
} else if (value_base_type == BASE_TYPE_VECTOR &&
|
|
field.value.type.element == BASE_TYPE_STRUCT) {
|
|
// Accessors for vectors of structs also take accessor objects,
|
|
// this generates a variant without that argument.
|
|
// ex: fun weapons(j: Int) = weapons(Weapon(), j)
|
|
GenerateFunOneLine(writer, field_name, "j: Int", opt_ret_type, [&]() {
|
|
writer += "{{field_name}}({{return_type}}(), j)";
|
|
});
|
|
}
|
|
|
|
if (IsScalar(value_base_type)) {
|
|
if (struct_def.fixed) {
|
|
GenerateGetterOneLine(writer, field_name, return_type, [&]() {
|
|
writer += "{{bbgetter}}(bb_pos + {{offset}}){{ucast}}";
|
|
});
|
|
} else {
|
|
GenerateGetter(writer, field_name, return_type, [&]() {
|
|
writer += "val o = __offset({{offset}})";
|
|
writer +=
|
|
"return if(o != 0) {{bbgetter}}"
|
|
"(o + bb_pos){{ucast}} else "
|
|
"{{field_default}}";
|
|
});
|
|
}
|
|
} else {
|
|
switch (value_base_type) {
|
|
case BASE_TYPE_STRUCT:
|
|
if (struct_def.fixed) {
|
|
// create getter with object reuse
|
|
// ex:
|
|
// fun pos(obj: Vec3) : Vec3? = obj.__assign(bb_pos + 4, bb)
|
|
// ? adds nullability annotation
|
|
GenerateFunOneLine(
|
|
writer, field_name, "obj: " + field_type, return_type + "?",
|
|
[&]() { writer += "obj.__assign(bb_pos + {{offset}}, bb)"; });
|
|
} else {
|
|
// create getter with object reuse
|
|
// ex:
|
|
// fun pos(obj: Vec3) : Vec3? {
|
|
// val o = __offset(4)
|
|
// return if(o != 0) {
|
|
// obj.__assign(o + bb_pos, bb)
|
|
// else {
|
|
// null
|
|
// }
|
|
// }
|
|
// ? adds nullability annotation
|
|
GenerateFun(
|
|
writer, field_name, "obj: " + field_type, return_type + "?",
|
|
[&]() {
|
|
auto fixed = field.value.type.struct_def->fixed;
|
|
|
|
writer.SetValue("seek", Indirect("o + bb_pos", fixed));
|
|
OffsetWrapper(
|
|
writer, offset_val,
|
|
[&]() { writer += "obj.__assign({{seek}}, bb)"; },
|
|
[&]() { writer += "null"; });
|
|
});
|
|
}
|
|
break;
|
|
case BASE_TYPE_STRING:
|
|
// create string getter
|
|
// e.g.
|
|
// val Name : String?
|
|
// get() = {
|
|
// val o = __offset(10)
|
|
// return if (o != 0) __string(o + bb_pos) else null
|
|
// }
|
|
// ? adds nullability annotation
|
|
GenerateGetter(writer, field_name, return_type + "?", [&]() {
|
|
writer += "val o = __offset({{offset}})";
|
|
writer += "return if (o != 0) __string(o + bb_pos) else null";
|
|
});
|
|
break;
|
|
case BASE_TYPE_VECTOR: {
|
|
// e.g.
|
|
// fun inventory(j: Int) : UByte {
|
|
// val o = __offset(14)
|
|
// return if (o != 0) {
|
|
// bb.get(__vector(o) + j * 1).toUByte()
|
|
// } else {
|
|
// 0
|
|
// }
|
|
// }
|
|
|
|
auto vectortype = field.value.type.VectorType();
|
|
std::string params = "j: Int";
|
|
std::string nullable = IsScalar(vectortype.base_type) ? "" : "?";
|
|
|
|
if (vectortype.base_type == BASE_TYPE_STRUCT ||
|
|
vectortype.base_type == BASE_TYPE_UNION) {
|
|
params = "obj: " + field_type + ", j: Int";
|
|
}
|
|
|
|
auto ret_type = return_type + nullable;
|
|
GenerateFun(writer, field_name, params, ret_type, [&]() {
|
|
auto inline_size = NumToString(InlineSize(vectortype));
|
|
auto index = "__vector(o) + j * " + inline_size;
|
|
auto not_found = NotFoundReturn(field.value.type.element);
|
|
auto found = "";
|
|
writer.SetValue("index", index);
|
|
switch (vectortype.base_type) {
|
|
case BASE_TYPE_STRUCT: {
|
|
bool fixed = vectortype.struct_def->fixed;
|
|
writer.SetValue("index", Indirect(index, fixed));
|
|
found = "obj.__assign({{index}}, bb)";
|
|
break;
|
|
}
|
|
case BASE_TYPE_UNION:
|
|
found = "{{bbgetter}}(obj, {{index}}){{ucast}}";
|
|
break;
|
|
default: found = "{{bbgetter}}({{index}}){{ucast}}";
|
|
}
|
|
OffsetWrapper(
|
|
writer, offset_val, [&]() { writer += found; },
|
|
[&]() { writer += not_found; });
|
|
});
|
|
break;
|
|
}
|
|
case BASE_TYPE_UNION:
|
|
GenerateFun(writer, field_name, "obj: " + field_type,
|
|
return_type + "?", [&]() {
|
|
writer += OffsetWrapperOneLine(
|
|
offset_val, bbgetter + "(obj, o + bb_pos)",
|
|
"null");
|
|
});
|
|
break;
|
|
default: FLATBUFFERS_ASSERT(0);
|
|
}
|
|
}
|
|
|
|
if (value_base_type == BASE_TYPE_VECTOR) {
|
|
// Generate Lenght functions for vectors
|
|
GenerateGetter(writer, field_name + "Length", "Int", [&]() {
|
|
writer += OffsetWrapperOneLine(offset_val, "__vector_len(o)", "0");
|
|
});
|
|
|
|
// See if we should generate a by-key accessor.
|
|
if (field.value.type.element == BASE_TYPE_STRUCT &&
|
|
!field.value.type.struct_def->fixed) {
|
|
auto &sd = *field.value.type.struct_def;
|
|
auto &fields = sd.fields.vec;
|
|
for (auto kit = fields.begin(); kit != fields.end(); ++kit) {
|
|
auto &kfield = **kit;
|
|
if (kfield.key) {
|
|
auto qualified_name = WrapInNameSpace(sd);
|
|
auto name = MakeCamel(Esc(field.name), false) + "ByKey";
|
|
auto params = "key: " + GenTypeGet(kfield.value.type);
|
|
auto rtype = qualified_name + "?";
|
|
GenerateFun(writer, name, params, rtype, [&]() {
|
|
OffsetWrapper(
|
|
writer, offset_val,
|
|
[&]() {
|
|
writer += qualified_name +
|
|
".__lookup_by_key(null, __vector(o), key, bb)";
|
|
},
|
|
[&]() { writer += "null"; });
|
|
});
|
|
|
|
auto param2 = "obj: " + qualified_name +
|
|
", key: " + GenTypeGet(kfield.value.type);
|
|
GenerateFun(writer, name, param2, rtype, [&]() {
|
|
OffsetWrapper(
|
|
writer, offset_val,
|
|
[&]() {
|
|
writer += qualified_name +
|
|
".__lookup_by_key(obj, __vector(o), key, bb)";
|
|
},
|
|
[&]() { writer += "null"; });
|
|
});
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((value_base_type == BASE_TYPE_VECTOR &&
|
|
IsScalar(field.value.type.VectorType().base_type)) ||
|
|
value_base_type == BASE_TYPE_STRING) {
|
|
auto end_idx =
|
|
NumToString(value_base_type == BASE_TYPE_STRING
|
|
? 1
|
|
: InlineSize(field.value.type.VectorType()));
|
|
// Generate a ByteBuffer accessor for strings & vectors of scalars.
|
|
// e.g.
|
|
// val inventoryByteBuffer: ByteBuffer
|
|
// get = __vector_as_bytebuffer(14, 1)
|
|
|
|
GenerateGetterOneLine(
|
|
writer, field_name + "AsByteBuffer", "ByteBuffer", [&]() {
|
|
writer.SetValue("end", end_idx);
|
|
writer += "__vector_as_bytebuffer({{offset}}, {{end}})";
|
|
});
|
|
|
|
// Generate a ByteBuffer accessor for strings & vectors of scalars.
|
|
// e.g.
|
|
// fun inventoryInByteBuffer(_bb: Bytebuffer):
|
|
// ByteBuffer = __vector_as_bytebuffer(_bb, 14, 1)
|
|
GenerateFunOneLine(
|
|
writer, field_name + "InByteBuffer", "_bb: ByteBuffer",
|
|
"ByteBuffer", [&]() {
|
|
writer.SetValue("end", end_idx);
|
|
writer += "__vector_in_bytebuffer(_bb, {{offset}}, {{end}})";
|
|
});
|
|
}
|
|
|
|
// generate object accessors if is nested_flatbuffer
|
|
// fun testnestedflatbufferAsMonster() : Monster?
|
|
//{ return testnestedflatbufferAsMonster(new Monster()); }
|
|
|
|
if (field.nested_flatbuffer) {
|
|
auto nested_type_name = WrapInNameSpace(*field.nested_flatbuffer);
|
|
auto nested_method_name =
|
|
field_name + "As" + field.nested_flatbuffer->name;
|
|
|
|
GenerateGetterOneLine(
|
|
writer, nested_method_name, nested_type_name + "?", [&]() {
|
|
writer += nested_method_name + "(" + nested_type_name + "())";
|
|
});
|
|
|
|
GenerateFun(writer, nested_method_name, "obj: " + nested_type_name,
|
|
nested_type_name + "?", [&]() {
|
|
OffsetWrapper(
|
|
writer, offset_val,
|
|
[&]() {
|
|
writer +=
|
|
"obj.__assign(__indirect(__vector(o)), bb)";
|
|
},
|
|
[&]() { writer += "null"; });
|
|
});
|
|
}
|
|
|
|
// Generate mutators for scalar fields or vectors of scalars.
|
|
if (parser_.opts.mutable_buffer) {
|
|
auto value_type = field.value.type;
|
|
auto underlying_type = value_base_type == BASE_TYPE_VECTOR
|
|
? value_type.VectorType()
|
|
: value_type;
|
|
auto name = "mutate" + MakeCamel(Esc(field.name), true);
|
|
auto size = NumToString(InlineSize(underlying_type));
|
|
auto params = Esc(field.name) + ": " + GenTypeGet(underlying_type);
|
|
// A vector mutator also needs the index of the vector element it should
|
|
// mutate.
|
|
if (value_base_type == BASE_TYPE_VECTOR) params.insert(0, "j: Int, ");
|
|
|
|
// Boolean parameters have to be explicitly converted to byte
|
|
// representation.
|
|
auto setter_parameter =
|
|
underlying_type.base_type == BASE_TYPE_BOOL
|
|
? "(if(" + Esc(field.name) + ") 1 else 0).toByte()"
|
|
: Esc(field.name);
|
|
|
|
auto setter_index =
|
|
value_base_type == BASE_TYPE_VECTOR
|
|
? "__vector(o) + j * " + size
|
|
: (struct_def.fixed ? "bb_pos + " + offset_val : "o + bb_pos");
|
|
if (IsScalar(value_base_type) ||
|
|
(value_base_type == BASE_TYPE_VECTOR &&
|
|
IsScalar(value_type.VectorType().base_type))) {
|
|
auto statements = [&]() {
|
|
writer.SetValue("bbsetter", ByteBufferSetter(underlying_type));
|
|
writer.SetValue("index", setter_index);
|
|
writer.SetValue("params", setter_parameter);
|
|
writer.SetValue("cast", CastToSigned(field));
|
|
if (struct_def.fixed) {
|
|
writer += "{{bbsetter}}({{index}}, {{params}}{{cast}})";
|
|
} else {
|
|
OffsetWrapper(
|
|
writer, offset_val,
|
|
[&]() {
|
|
writer += "{{bbsetter}}({{index}}, {{params}}{{cast}})";
|
|
writer += "true";
|
|
},
|
|
[&]() { writer += "false"; });
|
|
}
|
|
};
|
|
|
|
if (struct_def.fixed) {
|
|
GenerateFunOneLine(writer, name, params, "ByteBuffer", statements);
|
|
} else {
|
|
GenerateFun(writer, name, params, "Boolean", statements);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (struct_def.has_key && !struct_def.fixed) {
|
|
// Key Comparison method
|
|
GenerateOverrideFun(
|
|
writer, "keysCompare", "o1: Int, o2: Int, _bb: ByteBuffer", "Int",
|
|
[&]() {
|
|
if (key_field->value.type.base_type == BASE_TYPE_STRING) {
|
|
writer.SetValue("offset", NumToString(key_field->value.offset));
|
|
writer +=
|
|
" return compareStrings(__offset({{offset}}, o1, "
|
|
"_bb), __offset({{offset}}, o2, _bb), _bb)";
|
|
|
|
} else {
|
|
auto getter1 = GenLookupByKey(key_field, "_bb", "o1");
|
|
auto getter2 = GenLookupByKey(key_field, "_bb", "o2");
|
|
writer += "val val_1 = " + getter1;
|
|
writer += "val val_2 = " + getter2;
|
|
writer += "return (val_1 - val_2).sign";
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
static std::string CastToUsigned(const FieldDef &field) {
|
|
return CastToUsigned(field.value.type);
|
|
}
|
|
|
|
static std::string CastToUsigned(const Type type) {
|
|
switch (type.base_type) {
|
|
case BASE_TYPE_UINT: return ".toUInt()";
|
|
case BASE_TYPE_UCHAR:
|
|
case BASE_TYPE_UTYPE: return ".toUByte()";
|
|
case BASE_TYPE_USHORT: return ".toUShort()";
|
|
case BASE_TYPE_ULONG: return ".toULong()";
|
|
case BASE_TYPE_VECTOR: return CastToUsigned(type.VectorType());
|
|
default: return "";
|
|
}
|
|
}
|
|
|
|
static std::string CastToSigned(const FieldDef &field) {
|
|
return CastToSigned(field.value.type);
|
|
}
|
|
|
|
static std::string CastToSigned(const Type type) {
|
|
switch (type.base_type) {
|
|
case BASE_TYPE_UINT: return ".toInt()";
|
|
case BASE_TYPE_UCHAR:
|
|
case BASE_TYPE_UTYPE: return ".toByte()";
|
|
case BASE_TYPE_USHORT: return ".toShort()";
|
|
case BASE_TYPE_ULONG: return ".toLong()";
|
|
case BASE_TYPE_VECTOR: return CastToSigned(type.VectorType());
|
|
default: return "";
|
|
}
|
|
}
|
|
|
|
static std::string LiteralSuffix(const BaseType type) {
|
|
switch (type) {
|
|
case BASE_TYPE_UINT:
|
|
case BASE_TYPE_UCHAR:
|
|
case BASE_TYPE_UTYPE:
|
|
case BASE_TYPE_USHORT: return "u";
|
|
case BASE_TYPE_ULONG: return "UL";
|
|
case BASE_TYPE_LONG: return "L";
|
|
default: return "";
|
|
}
|
|
}
|
|
|
|
void GenerateCompanionObject(CodeWriter &code,
|
|
const std::function<void()> &callback) const {
|
|
code += "companion object {";
|
|
code.IncrementIdentLevel();
|
|
callback();
|
|
code.DecrementIdentLevel();
|
|
code += "}";
|
|
}
|
|
|
|
// Generate a documentation comment, if available.
|
|
void GenerateComment(const std::vector<std::string> &dc, CodeWriter &writer,
|
|
const CommentConfig *config) const {
|
|
if (dc.begin() == dc.end()) {
|
|
// Don't output empty comment blocks with 0 lines of comment content.
|
|
return;
|
|
}
|
|
|
|
if (config != nullptr && config->first_line != nullptr) {
|
|
writer += std::string(config->first_line);
|
|
}
|
|
std::string line_prefix =
|
|
((config != nullptr && config->content_line_prefix != nullptr)
|
|
? config->content_line_prefix
|
|
: "///");
|
|
for (auto it = dc.begin(); it != dc.end(); ++it) {
|
|
writer += line_prefix + *it;
|
|
}
|
|
if (config != nullptr && config->last_line != nullptr) {
|
|
writer += std::string(config->last_line);
|
|
}
|
|
}
|
|
|
|
static void GenerateGetRootAsAccessors(const std::string &struct_name,
|
|
CodeWriter &writer,
|
|
IDLOptions options) {
|
|
// Generate a special accessor for the table that when used as the root
|
|
// ex: fun getRootAsMonster(_bb: ByteBuffer): Monster {...}
|
|
writer.SetValue("gr_name", struct_name);
|
|
writer.SetValue("gr_method", "getRootAs" + struct_name);
|
|
|
|
// create convenience method that doesn't require an existing object
|
|
GenerateJvmStaticAnnotation(writer, options.gen_jvmstatic);
|
|
writer += "fun {{gr_method}}(_bb: ByteBuffer): {{gr_name}} = \\";
|
|
writer += "{{gr_method}}(_bb, {{gr_name}}())";
|
|
|
|
// create method that allows object reuse
|
|
// ex: fun Monster getRootAsMonster(_bb: ByteBuffer, obj: Monster) {...}
|
|
GenerateJvmStaticAnnotation(writer, options.gen_jvmstatic);
|
|
writer +=
|
|
"fun {{gr_method}}"
|
|
"(_bb: ByteBuffer, obj: {{gr_name}}): {{gr_name}} {";
|
|
writer.IncrementIdentLevel();
|
|
writer += "_bb.order(ByteOrder.LITTLE_ENDIAN)";
|
|
writer +=
|
|
"return (obj.__assign(_bb.getInt(_bb.position())"
|
|
" + _bb.position(), _bb))";
|
|
writer.DecrementIdentLevel();
|
|
writer += "}";
|
|
}
|
|
|
|
static void GenerateStaticConstructor(const StructDef &struct_def,
|
|
CodeWriter &code,
|
|
const IDLOptions options) {
|
|
// create a struct constructor function
|
|
auto params = StructConstructorParams(struct_def);
|
|
GenerateFun(code, "create" + Esc(struct_def.name), params, "Int", [&]() {
|
|
GenStructBody(struct_def, code, "");
|
|
code += "return builder.offset()";
|
|
}, options.gen_jvmstatic);
|
|
}
|
|
|
|
static std::string StructConstructorParams(const StructDef &struct_def,
|
|
const std::string &prefix = "") {
|
|
// builder: FlatBufferBuilder
|
|
std::stringstream out;
|
|
auto field_vec = struct_def.fields.vec;
|
|
if (prefix.empty()) { out << "builder: FlatBufferBuilder"; }
|
|
for (auto it = field_vec.begin(); it != field_vec.end(); ++it) {
|
|
auto &field = **it;
|
|
if (IsStruct(field.value.type)) {
|
|
// Generate arguments for a struct inside a struct. To ensure
|
|
// names don't clash, and to make it obvious these arguments are
|
|
// constructing a nested struct, prefix the name with the field
|
|
// name.
|
|
out << StructConstructorParams(*field.value.type.struct_def,
|
|
prefix + (Esc(field.name) + "_"));
|
|
} else {
|
|
out << ", " << prefix << MakeCamel(Esc(field.name), false) << ": "
|
|
<< GenTypeBasic(field.value.type.base_type);
|
|
}
|
|
}
|
|
return out.str();
|
|
}
|
|
|
|
static void GeneratePropertyOneLine(CodeWriter &writer,
|
|
const std::string &name,
|
|
const std::string &type,
|
|
const std::function<void()> &body) {
|
|
// Generates Kotlin getter for properties
|
|
// e.g.:
|
|
// val prop: Mytype = x
|
|
writer.SetValue("_name", name);
|
|
writer.SetValue("_type", type);
|
|
writer += "val {{_name}} : {{_type}} = \\";
|
|
body();
|
|
}
|
|
static void GenerateGetterOneLine(CodeWriter &writer, const std::string &name,
|
|
const std::string &type,
|
|
const std::function<void()> &body) {
|
|
// Generates Kotlin getter for properties
|
|
// e.g.:
|
|
// val prop: Mytype get() = x
|
|
writer.SetValue("_name", name);
|
|
writer.SetValue("_type", type);
|
|
writer += "val {{_name}} : {{_type}} get() = \\";
|
|
body();
|
|
}
|
|
|
|
static void GenerateGetter(CodeWriter &writer, const std::string &name,
|
|
const std::string &type,
|
|
const std::function<void()> &body) {
|
|
// Generates Kotlin getter for properties
|
|
// e.g.:
|
|
// val prop: Mytype
|
|
// get() = {
|
|
// return x
|
|
// }
|
|
writer.SetValue("name", name);
|
|
writer.SetValue("type", type);
|
|
writer += "val {{name}} : {{type}}";
|
|
writer.IncrementIdentLevel();
|
|
writer += "get() {";
|
|
writer.IncrementIdentLevel();
|
|
body();
|
|
writer.DecrementIdentLevel();
|
|
writer += "}";
|
|
writer.DecrementIdentLevel();
|
|
}
|
|
|
|
static void GenerateFun(CodeWriter &writer, const std::string &name,
|
|
const std::string ¶ms,
|
|
const std::string &returnType,
|
|
const std::function<void()> &body,
|
|
bool gen_jvmstatic = false) {
|
|
// Generates Kotlin function
|
|
// e.g.:
|
|
// fun path(j: Int): Vec3 {
|
|
// return path(Vec3(), j)
|
|
// }
|
|
auto noreturn = returnType.empty();
|
|
writer.SetValue("name", name);
|
|
writer.SetValue("params", params);
|
|
writer.SetValue("return_type", noreturn ? "" : ": " + returnType);
|
|
GenerateJvmStaticAnnotation(writer, gen_jvmstatic);
|
|
writer += "fun {{name}}({{params}}) {{return_type}} {";
|
|
writer.IncrementIdentLevel();
|
|
body();
|
|
writer.DecrementIdentLevel();
|
|
writer += "}";
|
|
}
|
|
|
|
static void GenerateFunOneLine(CodeWriter &writer, const std::string &name,
|
|
const std::string ¶ms,
|
|
const std::string &returnType,
|
|
const std::function<void()> &body,
|
|
bool gen_jvmstatic = false) {
|
|
// Generates Kotlin function
|
|
// e.g.:
|
|
// fun path(j: Int): Vec3 = return path(Vec3(), j)
|
|
writer.SetValue("name", name);
|
|
writer.SetValue("params", params);
|
|
writer.SetValue("return_type_p",
|
|
returnType.empty() ? "" : " : " + returnType);
|
|
GenerateJvmStaticAnnotation(writer, gen_jvmstatic);
|
|
writer += "fun {{name}}({{params}}){{return_type_p}} = \\";
|
|
body();
|
|
}
|
|
|
|
static void GenerateOverrideFun(CodeWriter &writer, const std::string &name,
|
|
const std::string ¶ms,
|
|
const std::string &returnType,
|
|
const std::function<void()> &body) {
|
|
// Generates Kotlin function
|
|
// e.g.:
|
|
// override fun path(j: Int): Vec3 = return path(Vec3(), j)
|
|
writer += "override \\";
|
|
GenerateFun(writer, name, params, returnType, body);
|
|
}
|
|
|
|
static void GenerateOverrideFunOneLine(CodeWriter &writer,
|
|
const std::string &name,
|
|
const std::string ¶ms,
|
|
const std::string &returnType,
|
|
const std::string &statement) {
|
|
// Generates Kotlin function
|
|
// e.g.:
|
|
// override fun path(j: Int): Vec3 = return path(Vec3(), j)
|
|
writer.SetValue("name", name);
|
|
writer.SetValue("params", params);
|
|
writer.SetValue("return_type",
|
|
returnType.empty() ? "" : " : " + returnType);
|
|
writer += "override fun {{name}}({{params}}){{return_type}} = \\";
|
|
writer += statement;
|
|
}
|
|
|
|
static std::string OffsetWrapperOneLine(const std::string &offset,
|
|
const std::string &found,
|
|
const std::string ¬_found) {
|
|
return "val o = __offset(" + offset + "); return if (o != 0) " + found +
|
|
" else " + not_found;
|
|
}
|
|
|
|
static void OffsetWrapper(CodeWriter &code, const std::string &offset,
|
|
const std::function<void()> &found,
|
|
const std::function<void()> ¬_found) {
|
|
code += "val o = __offset(" + offset + ")";
|
|
code += "return if (o != 0) {";
|
|
code.IncrementIdentLevel();
|
|
found();
|
|
code.DecrementIdentLevel();
|
|
code += "} else {";
|
|
code.IncrementIdentLevel();
|
|
not_found();
|
|
code.DecrementIdentLevel();
|
|
code += "}";
|
|
}
|
|
|
|
static std::string Indirect(const std::string &index, bool fixed) {
|
|
// We apply __indirect() and struct is not fixed.
|
|
if (!fixed) return "__indirect(" + index + ")";
|
|
return index;
|
|
}
|
|
|
|
static std::string NotFoundReturn(BaseType el) {
|
|
switch (el) {
|
|
case BASE_TYPE_FLOAT: return "0.0f";
|
|
case BASE_TYPE_DOUBLE: return "0.0";
|
|
case BASE_TYPE_BOOL: return "false";
|
|
case BASE_TYPE_LONG:
|
|
case BASE_TYPE_INT:
|
|
case BASE_TYPE_CHAR:
|
|
case BASE_TYPE_SHORT: return "0";
|
|
case BASE_TYPE_UINT:
|
|
case BASE_TYPE_UCHAR:
|
|
case BASE_TYPE_USHORT:
|
|
case BASE_TYPE_UTYPE: return "0u";
|
|
case BASE_TYPE_ULONG: return "0uL";
|
|
default: return "null";
|
|
}
|
|
}
|
|
|
|
// Prepend @JvmStatic to methods in companion object.
|
|
static void GenerateJvmStaticAnnotation(CodeWriter &code,
|
|
bool gen_jvmstatic) {
|
|
if (gen_jvmstatic) {
|
|
code += "@JvmStatic";
|
|
}
|
|
}
|
|
|
|
// This tracks the current namespace used to determine if a type need to be
|
|
// prefixed by its namespace
|
|
const Namespace *cur_name_space_;
|
|
};
|
|
} // namespace kotlin
|
|
|
|
bool GenerateKotlin(const Parser &parser, const std::string &path,
|
|
const std::string &file_name) {
|
|
kotlin::KotlinGenerator generator(parser, path, file_name);
|
|
return generator.generate();
|
|
}
|
|
} // namespace flatbuffers
|