加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
idl_gen_kotlin.cpp 62.69 KB
一键复制 编辑 原始数据 按行查看 历史
Derek Bailey 提交于 2023-05-26 10:33 . FlatBuffers Version 23.5.26 (#7976)
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657
/*
* 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 "idl_gen_kotlin.h"
#include <functional>
#include <unordered_set>
#include "flatbuffers/code_generators.h"
#include "flatbuffers/flatbuffers.h"
#include "flatbuffers/idl.h"
#include "flatbuffers/util.h"
#include "idl_namer.h"
namespace flatbuffers {
namespace kotlin {
namespace {
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 std::set<std::string> KotlinKeywords() {
return { "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" };
}
static Namer::Config KotlinDefaultConfig() {
return { /*types=*/Case::kKeep,
/*constants=*/Case::kKeep,
/*methods=*/Case::kLowerCamel,
/*functions=*/Case::kKeep,
/*fields=*/Case::kLowerCamel,
/*variables=*/Case::kLowerCamel,
/*variants=*/Case::kKeep,
/*enum_variant_seperator=*/"", // I.e. Concatenate.
/*escape_keywords=*/Namer::Config::Escape::BeforeConvertingCase,
/*namespaces=*/Case::kKeep,
/*namespace_seperator=*/"__",
/*object_prefix=*/"",
/*object_suffix=*/"T",
/*keyword_prefix=*/"",
/*keyword_suffix=*/"_",
/*filenames=*/Case::kKeep,
/*directories=*/Case::kKeep,
/*output_path=*/"",
/*filename_suffix=*/"",
/*filename_extension=*/".kt" };
}
} // namespace
class KotlinGenerator : public BaseGenerator {
public:
KotlinGenerator(const Parser &parser, const std::string &path,
const std::string &file_name)
: BaseGenerator(parser, path, file_name, "", ".", "kt"),
namer_(WithFlagOptions(KotlinDefaultConfig(), parser.opts, path),
KotlinKeywords()) {}
KotlinGenerator &operator=(const KotlinGenerator &);
bool generate() FLATBUFFERS_OVERRIDE {
std::string one_file_code;
for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
++it) {
CodeWriter enumWriter(ident_pad);
auto &enum_def = **it;
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;
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 com.google.flatbuffers.BaseVector\n"
"import com.google.flatbuffers.BooleanVector\n"
"import com.google.flatbuffers.ByteVector\n"
"import com.google.flatbuffers.Constants\n"
"import com.google.flatbuffers.DoubleVector\n"
"import com.google.flatbuffers.FlatBufferBuilder\n"
"import com.google.flatbuffers.FloatVector\n"
"import com.google.flatbuffers.LongVector\n"
"import com.google.flatbuffers.StringVector\n"
"import com.google.flatbuffers.Struct\n"
"import com.google.flatbuffers.Table\n"
"import com.google.flatbuffers.UnionVector\n"
"import java.nio.ByteBuffer\n"
"import java.nio.ByteOrder\n"
"import kotlin.math.sign\n\n";
}
code += classcode;
const std::string dirs = namer_.Directories(ns);
EnsureDirExists(dirs);
const std::string filename =
dirs + namer_.File(defname, /*skips=*/SkipFile::Suffix);
return SaveFile(filename.c_str(), code, false);
}
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";
}
}
// with the addition of optional scalar types,
// we are adding the nullable '?' operator to return type of a field.
std::string GetterReturnType(const FieldDef &field) const {
auto base_type = field.value.type.base_type;
auto r_type = GenTypeGet(field.value.type);
if (field.IsScalarOptional() ||
// string, structs and unions
(!field.IsRequired() &&
(base_type == BASE_TYPE_STRING || base_type == BASE_TYPE_STRUCT ||
base_type == BASE_TYPE_UNION)) ||
// vector of anything not scalar
(base_type == BASE_TYPE_VECTOR && !field.IsRequired() &&
!IsScalar(field.value.type.VectorType().base_type))) {
r_type += "?";
}
return r_type;
}
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 {
if (field.IsScalarOptional()) {
// although default value is null, java API forces us to present a real
// default value for scalars, while adding a field to the buffer. This is
// not a problem because the default can be representing just by not
// calling builder.addMyField()
switch (field.value.type.base_type) {
case BASE_TYPE_DOUBLE:
case BASE_TYPE_FLOAT: return "0.0";
case BASE_TYPE_BOOL: return "false";
default: return "0";
}
}
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 (field.IsScalarOptional()) { return "null"; }
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 += "class " + namer_.Type(enum_def) + " 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", namer_.Variant(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;
bool generate_names =
range / static_cast<uint64_t>(enum_def.size()) < kMaxSparseness &&
parser_.opts.mini_reflect == IDLOptions::kTypesAndNames;
if (generate_names) {
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 + "." +
namer_.Method("get", 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." + namer_.Method("put", 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.
void GenStructArgs(const StructDef &struct_def, CodeWriter &writer,
const char *nameprefix) const {
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 += namer_.Field(field) + ": \\";
writer += GenTypeBasic(field.value.type.base_type) + "\\";
}
}
}
// Recusively generate struct construction statements of the form:
// builder.putType(name);
// and insert manual padding.
void GenStructBody(const StructDef &struct_def, CodeWriter &writer,
const char *nameprefix) const {
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 + namer_.Variable(field));
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", namer_.Type(struct_def));
writer.SetValue("superclass", fixed ? "Struct" : "Table");
writer += "@Suppress(\"unused\")";
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",
namer_.Type(struct_def), [&]() {
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 version check method.
// Force compile time error if not using the same version
// runtime.
GenerateFunOneLine(
writer, "validateVersion", "", "",
[&]() { writer += "Constants.FLATBUFFERS_23_5_26()"; },
options.gen_jvmstatic);
GenerateGetRootAsAccessors(namer_.Type(struct_def), 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 (IsVector(field.value.type)) {
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: " << namer_.Type(struct_def) << "?"
<< ", ";
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", namer_.Type(struct_def));
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 (IsString(key_field->value.type)) {
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(),
namer_.Type(struct_def) + "?", 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 =
namer_.LegacyJavaMethod2("finishSizePrefixed", struct_def, "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 =
namer_.LegacyKotlinMethod("finish", struct_def, "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 = namer_.LegacyJavaMethod2("end", struct_def, "");
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.IsRequired()) { 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 = namer_.Method("create", field, "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));
if (IsUnsigned(vector_type.base_type)) {
writer += "@kotlin.ExperimentalUnsignedTypes";
}
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, namer_.Method("start", field, "Vector"), 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 = namer_.Variable(field.name) + ": " + field_type;
auto content = [&]() {
auto method = GenMethod(field.value.type);
writer.SetValue("field_name", namer_.Field(field));
writer.SetValue("method_name", method);
writer.SetValue("pos", field_pos);
writer.SetValue("default", GenFBBDefaultValue(field));
writer.SetValue("cast", GenFBBValueCast(field));
if (field.key) {
// field has key attribute, so always need to exist
// even if its value is equal to default.
// Generated code will bypass default checking
// resulting in { builder.addShort(name); slot(id); }
writer += "builder.add{{method_name}}({{field_name}}{{cast}})";
writer += "builder.slot({{pos}})";
} else {
writer += "builder.add{{method_name}}({{pos}}, \\";
writer += "{{field_name}}{{cast}}, {{default}})";
}
};
auto signature = namer_.LegacyKotlinMethod("add", field, "");
auto params = "builder: FlatBufferBuilder, " + secondArg;
if (field.key) {
GenerateFun(writer, signature, params, "", content,
options.gen_jvmstatic);
} else {
GenerateFunOneLine(writer, signature, params, "", content,
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, namer_.LegacyJavaMethod2("start", struct_def, ""),
"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 = namer_.LegacyJavaMethod2("create", struct_def, "");
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 << ", " << namer_.Variable(field);
if (!IsScalar(field.value.type.base_type)) {
params << "Offset: ";
} else {
params << ": ";
}
auto optional = field.IsScalarOptional() ? "?" : "";
params << GenTypeBasic(field.value.type.base_type) << optional;
}
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("field_name", namer_.Field(field));
// we wrap on null check for scalar optionals
writer += field.IsScalarOptional()
? "{{field_name}}?.run { \\"
: "\\";
writer += namer_.LegacyKotlinMethod("add", field, "") +
"(builder, {{field_name}}\\";
if (!IsScalar(field.value.type.base_type)) {
writer += "Offset\\";
}
// we wrap on null check for scalar optionals
writer += field.IsScalarOptional() ? ") }" : ")";
}
}
}
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 = namer_.Function(struct_def);
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 = namer_.Field(field);
auto field_type = GenTypeGet(field.value.type);
auto field_default_value = GenDefaultValue(field);
auto return_type = GetterReturnType(field);
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);
// 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, return_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", return_type, [&]() {
writer += "{{field_name}}({{field_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)"; },
[&]() {
if (field.IsRequired()) {
writer +=
"throw AssertionError(\"No value for "
"(required) field {{field_name}}\")";
} else {
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) {";
writer.IncrementIdentLevel();
writer += "__string(o + bb_pos)";
writer.DecrementIdentLevel();
writer += "} else {";
writer.IncrementIdentLevel();
if (field.IsRequired()) {
writer +=
"throw AssertionError(\"No value for (required) field "
"{{field_name}}\")";
} else {
writer += "null";
}
writer.DecrementIdentLevel();
writer += "}";
});
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";
if (vectortype.base_type == BASE_TYPE_STRUCT ||
vectortype.base_type == BASE_TYPE_UNION) {
params = "obj: " + field_type + ", j: Int";
}
GenerateFun(writer, field_name, params, return_type, [&]() {
auto inline_size = NumToString(InlineSize(vectortype));
auto index = "__vector(o) + j * " + inline_size;
auto not_found =
field.IsRequired()
? "throw IndexOutOfBoundsException(\"Index out of range: "
"$j, vector {{field_name}} is empty\")"
: 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 = namer_.Method(field, "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 = namer_.LegacyKotlinMethod("mutate", field, "");
auto size = NumToString(InlineSize(underlying_type));
auto params = namer_.Field(field) + ": " + 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(" + namer_.Field(field) + ") 1 else 0).toByte()"
: namer_.Field(field);
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 (IsString(key_field->value.type)) {
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);
}
}
void GenerateGetRootAsAccessors(const std::string &struct_name,
CodeWriter &writer,
IDLOptions options) const {
// 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 += "}";
}
void GenerateStaticConstructor(const StructDef &struct_def, CodeWriter &code,
const IDLOptions options) const {
// create a struct constructor function
auto params = StructConstructorParams(struct_def);
GenerateFun(
code, namer_.LegacyJavaMethod2("create", struct_def, ""), params, "Int",
[&]() {
GenStructBody(struct_def, code, "");
code += "return builder.offset()";
},
options.gen_jvmstatic);
}
std::string StructConstructorParams(const StructDef &struct_def,
const std::string &prefix = "") const {
// 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 + (namer_.Variable(field) + "_"));
} else {
out << ", " << prefix << namer_.Variable(field) << ": "
<< 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 &params,
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 &params,
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 &params,
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 &params,
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 &not_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()> &not_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"; }
}
const IdlNamer namer_;
};
} // namespace kotlin
static 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 {
class KotlinCodeGenerator : public CodeGenerator {
public:
Status GenerateCode(const Parser &parser, const std::string &path,
const std::string &filename) override {
if (!GenerateKotlin(parser, path, filename)) { return Status::ERROR; }
return Status::OK;
}
Status GenerateCode(const uint8_t *, int64_t,
const CodeGenOptions &) override {
return Status::NOT_IMPLEMENTED;
}
Status GenerateMakeRule(const Parser &parser, const std::string &path,
const std::string &filename,
std::string &output) override {
(void)parser;
(void)path;
(void)filename;
(void)output;
return Status::NOT_IMPLEMENTED;
}
Status GenerateGrpcCode(const Parser &parser, const std::string &path,
const std::string &filename) override {
(void)parser;
(void)path;
(void)filename;
return Status::NOT_IMPLEMENTED;
}
Status GenerateRootFile(const Parser &parser,
const std::string &path) override {
(void)parser;
(void)path;
return Status::NOT_IMPLEMENTED;
}
bool IsSchemaOnly() const override { return true; }
bool SupportsBfbsGeneration() const override { return false; }
bool SupportsRootFileGeneration() const override { return false; }
IDLOptions::Language Language() const override { return IDLOptions::kKotlin; }
std::string LanguageName() const override { return "Kotlin"; }
};
} // namespace
std::unique_ptr<CodeGenerator> NewKotlinCodeGenerator() {
return std::unique_ptr<KotlinCodeGenerator>(new KotlinCodeGenerator());
}
} // namespace flatbuffers
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化