diff --git a/docs/source/Compiler.md b/docs/source/Compiler.md index 87b4275c4..e5d065c4b 100644 --- a/docs/source/Compiler.md +++ b/docs/source/Compiler.md @@ -116,6 +116,9 @@ Additional options: - `--gen-generated` : Add @Generated annotation for Java. +- `--gen-jvmstatic` : Add @JvmStatic annotation for Kotlin methods + in companion object for interop from Java to Kotlin. + - `--gen-all` : Generate not just code for the current schema files, but for all files it includes as well. If the language uses a single file for output (by default the case for C++ and JS), all code will end up in diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index 70a2b787b..847c976a1 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -509,6 +509,7 @@ struct ServiceDef : public Definition { // Container of options that may apply to any of the source/text generators. struct IDLOptions { + bool gen_jvmstatic; // Use flexbuffers instead for binary and text generation bool use_flexbuffers; bool strict_json; @@ -604,7 +605,8 @@ struct IDLOptions { bool set_empty_vectors_to_null; IDLOptions() - : use_flexbuffers(false), + : gen_jvmstatic(false), + use_flexbuffers(false), strict_json(false), skip_js_exports(false), use_goog_js_export_format(false), diff --git a/src/flatc.cpp b/src/flatc.cpp index 947e78d7a..4a77cce5e 100644 --- a/src/flatc.cpp +++ b/src/flatc.cpp @@ -106,6 +106,8 @@ std::string FlatCompiler::GetUsageString(const char *program_name) const { " --gen-nullable Add Clang _Nullable for C++ pointer. or @Nullable for Java\n" " --java-checkerframe work Add @Pure for Java.\n" " --gen-generated Add @Generated annotation for Java\n" + " --gen-jvmstatic Add @JvmStatic annotation for Kotlin methods\n" + " in companion object for interop from Java to Kotlin.\n" " --gen-all Generate not just code for the current schema files,\n" " but for all files it includes as well.\n" " If the language uses a single file for output (by default\n" @@ -363,6 +365,8 @@ int FlatCompiler::Compile(int argc, const char **argv) { opts.cs_gen_json_serializer = true; } else if (arg == "--flexbuffers") { opts.use_flexbuffers = true; + } else if (arg == "--gen-jvmstatic") { + opts.gen_jvmstatic = true; } else if (arg == "--cpp-std") { if (++argi >= argc) Error("missing C++ standard specification" + arg, true); diff --git a/src/idl_gen_kotlin.cpp b/src/idl_gen_kotlin.cpp index df032f776..2c0c8d433 100644 --- a/src/idl_gen_kotlin.cpp +++ b/src/idl_gen_kotlin.cpp @@ -88,7 +88,7 @@ class KotlinGenerator : public BaseGenerator { auto &struct_def = **it; if (!parser_.opts.one_file) cur_name_space_ = struct_def.defined_namespace; - GenStruct(struct_def, structWriter); + GenStruct(struct_def, structWriter, parser_.opts); if (parser_.opts.one_file) { one_file_code += structWriter.ToString(); } else { @@ -276,7 +276,7 @@ class KotlinGenerator : public BaseGenerator { if (enum_def.MinValue()->IsNonZero()) writer += " - " + enum_def.MinValue()->name + ".toInt()\\"; writer += "]"; - }); + }, parser_.opts.gen_jvmstatic); } }); writer.DecrementIdentLevel(); @@ -417,7 +417,7 @@ class KotlinGenerator : public BaseGenerator { return key_offset; } - void GenStruct(StructDef &struct_def, CodeWriter &writer) const { + void GenStruct(StructDef &struct_def, CodeWriter &writer, IDLOptions options) const { if (struct_def.generated) return; GenerateComment(struct_def.doc_comment, writer, &comment_config); @@ -458,13 +458,13 @@ class KotlinGenerator : public BaseGenerator { // runtime. GenerateFunOneLine(writer, "validateVersion", "", "", [&]() { writer += "Constants.FLATBUFFERS_1_12_0()"; - }); + }, options.gen_jvmstatic); - GenerateGetRootAsAccessors(Esc(struct_def.name), writer); - GenerateBufferHasIdentifier(struct_def, writer); - GenerateTableCreator(struct_def, writer); + GenerateGetRootAsAccessors(Esc(struct_def.name), writer, options); + GenerateBufferHasIdentifier(struct_def, writer, options); + GenerateTableCreator(struct_def, writer, options); - GenerateStartStructMethod(struct_def, writer); + GenerateStartStructMethod(struct_def, writer, options); // Static Add for fields auto fields = struct_def.fields.vec; @@ -474,29 +474,29 @@ class KotlinGenerator : public BaseGenerator { field_pos++; if (field.deprecated) continue; if (field.key) key_field = &field; - GenerateAddField(NumToString(field_pos), field, writer); + 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); + GenerateCreateVectorField(field, writer, options); } - GenerateStartVectorField(field, writer); + GenerateStartVectorField(field, writer, options); } } - GenerateEndStructMethod(struct_def, writer); + GenerateEndStructMethod(struct_def, writer, options); auto file_identifier = parser_.file_identifier_; if (parser_.root_struct_def_ == &struct_def) { - GenerateFinishStructBuffer(struct_def, file_identifier, writer); - GenerateFinishSizePrefixed(struct_def, file_identifier, writer); + 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); + GenerateLookupByKey(key_field, struct_def, writer, options); } } else { - GenerateStaticConstructor(struct_def, writer); + GenerateStaticConstructor(struct_def, writer, options); } }); } @@ -508,7 +508,7 @@ class KotlinGenerator : public BaseGenerator { // TODO: move key_field to reference instead of pointer void GenerateLookupByKey(FieldDef *key_field, StructDef &struct_def, - CodeWriter &writer) const { + CodeWriter &writer, const IDLOptions options) const { std::stringstream params; params << "obj: " << Esc(struct_def.name) << "?" << ", "; @@ -564,31 +564,34 @@ class KotlinGenerator : public BaseGenerator { writer += "return null"; }; GenerateFun(writer, "__lookup_by_key", params.str(), - Esc(struct_def.name) + "?", statements); + Esc(struct_def.name) + "?", statements, options.gen_jvmstatic); } void GenerateFinishSizePrefixed(StructDef &struct_def, const std::string &identifier, - CodeWriter &writer) const { + 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 { + 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 + ")"; }); + [&]() { writer += "builder.finish(offset" + id + ")"; }, + options.gen_jvmstatic); } - void GenerateEndStructMethod(StructDef &struct_def, - CodeWriter &writer) const { + 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"; @@ -606,11 +609,12 @@ class KotlinGenerator : public BaseGenerator { } 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 { + 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: " + @@ -628,10 +632,11 @@ class KotlinGenerator : public BaseGenerator { writer.DecrementIdentLevel(); writer += "}"; writer += "return builder.endVector()"; - }); + }, options.gen_jvmstatic); } - void GenerateStartVectorField(FieldDef &field, CodeWriter &writer) const { + 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(); @@ -643,11 +648,11 @@ class KotlinGenerator : public BaseGenerator { 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 { + 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), @@ -662,7 +667,7 @@ class KotlinGenerator : public BaseGenerator { writer += "builder.add{{method_name}}({{pos}}, \\"; writer += "{{field_name}}{{cast}}, {{default}})"; - }); + }, options.gen_jvmstatic); } static std::string ToSignedType(const Type &type) { @@ -703,17 +708,18 @@ class KotlinGenerator : public BaseGenerator { } // fun startMonster(builder: FlatBufferBuilder) = builder.startTable(11) - void GenerateStartStructMethod(StructDef &struct_def, - CodeWriter &code) const { + 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 { + 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. @@ -776,11 +782,11 @@ class KotlinGenerator : public BaseGenerator { } } writer += "return end{{struct_name}}(builder)"; - }); + }, options.gen_jvmstatic); } } void GenerateBufferHasIdentifier(StructDef &struct_def, - CodeWriter &writer) const { + 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()) @@ -790,7 +796,7 @@ class KotlinGenerator : public BaseGenerator { "Boolean", [&]() { writer += "__has_identifier(_bb, \"" + file_identifier + "\")"; - }); + }, options.gen_jvmstatic); } void GenerateStructGetters(StructDef &struct_def, CodeWriter &writer) const { @@ -1218,18 +1224,21 @@ class KotlinGenerator : public BaseGenerator { } static void GenerateGetRootAsAccessors(const std::string &struct_name, - CodeWriter &writer) { + 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}} {"; @@ -1243,13 +1252,14 @@ class KotlinGenerator : public BaseGenerator { } static void GenerateStaticConstructor(const StructDef &struct_def, - CodeWriter &code) { + 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, @@ -1323,7 +1333,8 @@ class KotlinGenerator : public BaseGenerator { static void GenerateFun(CodeWriter &writer, const std::string &name, const std::string ¶ms, const std::string &returnType, - const std::function &body) { + const std::function &body, + bool gen_jvmstatic = false) { // Generates Kotlin function // e.g.: // fun path(j: Int): Vec3 { @@ -1333,6 +1344,7 @@ class KotlinGenerator : public BaseGenerator { 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(); @@ -1343,7 +1355,8 @@ class KotlinGenerator : public BaseGenerator { static void GenerateFunOneLine(CodeWriter &writer, const std::string &name, const std::string ¶ms, const std::string &returnType, - const std::function &body) { + const std::function &body, + bool gen_jvmstatic = false) { // Generates Kotlin function // e.g.: // fun path(j: Int): Vec3 = return path(Vec3(), j) @@ -1351,6 +1364,7 @@ class KotlinGenerator : public BaseGenerator { writer.SetValue("params", params); writer.SetValue("return_type_p", returnType.empty() ? "" : " : " + returnType); + GenerateJvmStaticAnnotation(writer, gen_jvmstatic); writer += "fun {{name}}({{params}}){{return_type_p}} = \\"; body(); } @@ -1428,6 +1442,14 @@ class KotlinGenerator : public BaseGenerator { } } + // 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_;