From 1c068db5188e9a63bca33e53b85431a5610df9c9 Mon Sep 17 00:00:00 2001 From: Protobuf Team Bot Date: Mon, 30 Sep 2024 08:09:07 -0700 Subject: [PATCH] Moving Kotlin DSL message generators to the kotlin/ directory. Merge full and lite message generator into one while branching on if generating lite kotlin and/or jvm code. PiperOrigin-RevId: 680580214 --- src/google/protobuf/compiler/java/BUILD.bazel | 5 +- .../protobuf/compiler/java/field_common.cc | 2 +- .../protobuf/compiler/java/full/BUILD.bazel | 2 + .../compiler/java/full/field_generator.h | 1 - .../protobuf/compiler/java/full/message.cc | 300 ------------ .../protobuf/compiler/java/full/message.h | 5 - .../protobuf/compiler/java/generator_common.h | 1 + .../compiler/java/generator_factory.h | 3 - src/google/protobuf/compiler/java/helpers.h | 7 +- .../protobuf/compiler/java/lite/BUILD.bazel | 5 +- .../compiler/java/lite/field_generator.h | 15 +- .../protobuf/compiler/java/lite/message.cc | 369 --------------- .../protobuf/compiler/java/lite/message.h | 5 - .../protobuf/compiler/kotlin/BUILD.bazel | 18 +- src/google/protobuf/compiler/kotlin/file.cc | 30 +- src/google/protobuf/compiler/kotlin/file.h | 5 +- .../protobuf/compiler/kotlin/message.cc | 444 ++++++++++++++++++ src/google/protobuf/compiler/kotlin/message.h | 58 +++ 18 files changed, 550 insertions(+), 725 deletions(-) create mode 100644 src/google/protobuf/compiler/kotlin/message.cc create mode 100644 src/google/protobuf/compiler/kotlin/message.h diff --git a/src/google/protobuf/compiler/java/BUILD.bazel b/src/google/protobuf/compiler/java/BUILD.bazel index d1ac684ed4..7bb380d4e8 100644 --- a/src/google/protobuf/compiler/java/BUILD.bazel +++ b/src/google/protobuf/compiler/java/BUILD.bazel @@ -156,7 +156,10 @@ cc_library( "generator_factory.h", ], strip_include_prefix = "/src", - visibility = ["//src/google/protobuf/compiler/java:__subpackages__"], + visibility = [ + "//src/google/protobuf/compiler/java:__subpackages__", + "//src/google/protobuf/compiler/kotlin:__subpackages__", + ], deps = [ ":helpers", "//src/google/protobuf", diff --git a/src/google/protobuf/compiler/java/field_common.cc b/src/google/protobuf/compiler/java/field_common.cc index 7ff7f0cd48..29a712aaa6 100644 --- a/src/google/protobuf/compiler/java/field_common.cc +++ b/src/google/protobuf/compiler/java/field_common.cc @@ -41,7 +41,7 @@ void SetCommonFieldVariables( (*variables)["kt_capitalized_name"] = IsForbiddenKotlin(info->name) ? absl::StrCat(info->capitalized_name, "_") : info->capitalized_name; - (*variables)["jvm_synthetic"] = JvmSynthetic(info->options); + (*variables)["jvm_synthetic"] = JvmSynthetic(info->options.jvm_dsl); if (!descriptor->is_repeated()) { (*variables)["annotation_field_type"] = std::string(FieldTypeName(descriptor->type())); diff --git a/src/google/protobuf/compiler/java/full/BUILD.bazel b/src/google/protobuf/compiler/java/full/BUILD.bazel index bee46663d4..1fb6f079c4 100644 --- a/src/google/protobuf/compiler/java/full/BUILD.bazel +++ b/src/google/protobuf/compiler/java/full/BUILD.bazel @@ -4,6 +4,7 @@ cc_library( name = "fg", hdrs = ["field_generator.h"], strip_include_prefix = "/src", + visibility = ["//src/google/protobuf/compiler/kotlin:__subpackages__"], deps = [ "//src/google/protobuf/compiler/java:generator_common", "//src/google/protobuf/io:printer", @@ -49,6 +50,7 @@ cc_library( srcs = ["make_field_gens.cc"], hdrs = ["make_field_gens.h"], strip_include_prefix = "/src", + visibility = ["//src/google/protobuf/compiler/kotlin:__subpackages__"], deps = [ ":fg", ":fgs", diff --git a/src/google/protobuf/compiler/java/full/field_generator.h b/src/google/protobuf/compiler/java/full/field_generator.h index eb7a897ae5..9096567911 100644 --- a/src/google/protobuf/compiler/java/full/field_generator.h +++ b/src/google/protobuf/compiler/java/full/field_generator.h @@ -33,7 +33,6 @@ class ImmutableFieldGenerator : public FieldGenerator { virtual void GenerateSerializedSizeCode(io::Printer* printer) const = 0; virtual void GenerateFieldBuilderInitializationCode( io::Printer* printer) const = 0; - virtual void GenerateKotlinDslMembers(io::Printer* printer) const = 0; virtual void GenerateBuilderParsingCodeFromPacked( io::Printer* printer) const { diff --git a/src/google/protobuf/compiler/java/full/message.cc b/src/google/protobuf/compiler/java/full/message.cc index 887dfe2b2b..3e3e499993 100644 --- a/src/google/protobuf/compiler/java/full/message.cc +++ b/src/google/protobuf/compiler/java/full/message.cc @@ -1208,306 +1208,6 @@ void ImmutableMessageGenerator::GenerateMutableCopy(io::Printer* printer) { "mutable_name", name_resolver_->GetJavaMutableClassName(descriptor_)); } -void ImmutableMessageGenerator::GenerateKotlinDsl(io::Printer* printer) const { - printer->Print( - "@kotlin.OptIn" - "(com.google.protobuf.kotlin.OnlyForUseByGeneratedProtoCode::class)\n" - "@com.google.protobuf.kotlin.ProtoDslMarker\n"); - printer->Print( - "public class Dsl private constructor(\n" - " private val _builder: $message$.Builder\n" - ") {\n" - " public companion object {\n" - " @kotlin.jvm.JvmSynthetic\n" - " @kotlin.PublishedApi\n" - " internal fun _create(builder: $message$.Builder): Dsl = " - "Dsl(builder)\n" - " }\n" - "\n" - " @kotlin.jvm.JvmSynthetic\n" - " @kotlin.PublishedApi\n" - " internal fun _build(): $message$ = _builder.build()\n", - "message", - EscapeKotlinKeywords(name_resolver_->GetClassName(descriptor_, true))); - - printer->Indent(); - - for (int i = 0; i < descriptor_->field_count(); i++) { - printer->Print("\n"); - field_generators_.get(descriptor_->field(i)) - .GenerateKotlinDslMembers(printer); - } - - for (auto& kv : oneofs_) { - const OneofDescriptor* oneof = kv.second; - printer->Print( - "public val $oneof_name$Case: $message$.$oneof_capitalized_name$Case\n" - " @JvmName(\"get$oneof_capitalized_name$Case\")\n" - " get() = _builder.get$oneof_capitalized_name$Case()\n\n" - "public fun clear$oneof_capitalized_name$() {\n" - " _builder.clear$oneof_capitalized_name$()\n" - "}\n", - "oneof_name", context_->GetOneofGeneratorInfo(oneof)->name, - "oneof_capitalized_name", - context_->GetOneofGeneratorInfo(oneof)->capitalized_name, "message", - EscapeKotlinKeywords(name_resolver_->GetClassName(descriptor_, true))); - } - - if (descriptor_->extension_range_count() > 0) { - GenerateKotlinExtensions(printer); - } - - printer->Outdent(); - printer->Print("}\n"); -} - -void ImmutableMessageGenerator::GenerateKotlinMembers( - io::Printer* printer) const { - printer->Print("@kotlin.jvm.JvmName(\"-initialize$camelcase_name$\")\n", - "camelcase_name", - name_resolver_->GetKotlinFactoryName(descriptor_)); - - - printer->Print( - "public inline fun $camelcase_name$(block: $message_kt$.Dsl.() -> " - "kotlin.Unit): " - "$message$ " - "=\n" - " $message_kt$.Dsl._create($message$.newBuilder()).apply { block() " - "}._build()\n", - "camelcase_name", name_resolver_->GetKotlinFactoryName(descriptor_), - "message_kt", - EscapeKotlinKeywords( - name_resolver_->GetKotlinExtensionsClassName(descriptor_)), - "message", - EscapeKotlinKeywords(name_resolver_->GetClassName(descriptor_, true))); - - WriteMessageDocComment(printer, descriptor_, context_->options(), - /* kdoc */ true); - printer->Emit( - { - io::Printer::Sub{"name_kt", absl::StrCat(descriptor_->name(), "Kt")} - .AnnotatedAs(descriptor_), - {"body", - [&]() { - GenerateKotlinDsl(printer); - for (int i = 0; i < descriptor_->nested_type_count(); i++) { - if (IsMapEntry(descriptor_->nested_type(i))) continue; - ImmutableMessageGenerator(descriptor_->nested_type(i), context_) - .GenerateKotlinMembers(printer); - } - }}, - }, - R"kt( - public object $name_kt$ { - $body$; - } - )kt"); -} - -void ImmutableMessageGenerator::GenerateTopLevelKotlinMembers( - io::Printer* printer) const { - printer->Print("@kotlin.jvm.JvmSynthetic\n"); - - - printer->Print( - "public inline fun $message$.copy(block: $message_kt$.Dsl.() -> " - "kotlin.Unit): $message$ =\n" - " $message_kt$.Dsl._create(this.toBuilder()).apply { block() " - "}._build()\n\n", - "message", - EscapeKotlinKeywords(name_resolver_->GetClassName(descriptor_, true)), - "message_kt", - name_resolver_->GetKotlinExtensionsClassNameEscaped(descriptor_)); - - for (int i = 0; i < descriptor_->nested_type_count(); i++) { - if (IsMapEntry(descriptor_->nested_type(i))) continue; - ImmutableMessageGenerator(descriptor_->nested_type(i), context_) - .GenerateTopLevelKotlinMembers(printer); - } - - GenerateKotlinOrNull(printer); -} - -void ImmutableMessageGenerator::GenerateKotlinOrNull( - io::Printer* printer) const { - for (int i = 0; i < descriptor_->field_count(); i++) { - const FieldDescriptor* field = descriptor_->field(i); - if (field->has_presence() && GetJavaType(field) == JAVATYPE_MESSAGE) { - if (field->options().deprecated()) { - printer->Print( - "@kotlin.Deprecated(message = \"Field $name$ is deprecated\")\n", - "name", context_->GetFieldGeneratorInfo(field)->name); - } - printer->Print( - "public val $full_classname$OrBuilder.$camelcase_name$OrNull: " - "$full_name$?\n" - " get() = if (has$name$()) get$name$() else null\n\n", - "full_classname", - EscapeKotlinKeywords(name_resolver_->GetClassName(descriptor_, true)), - "camelcase_name", context_->GetFieldGeneratorInfo(field)->name, - "full_name", - EscapeKotlinKeywords( - name_resolver_->GetImmutableClassName(field->message_type())), - "name", context_->GetFieldGeneratorInfo(field)->capitalized_name); - } - } -} - -void ImmutableMessageGenerator::GenerateKotlinExtensions( - io::Printer* printer) const { - std::string message_name = - EscapeKotlinKeywords(name_resolver_->GetClassName(descriptor_, true)); - - printer->Print( - "@Suppress(\"UNCHECKED_CAST\")\n" - "@kotlin.jvm.JvmSynthetic\n" - "public operator fun get(extension: " - "com.google.protobuf.ExtensionLite<$message$, T>): T {\n" - " return if (extension.isRepeated) {\n" - " get(extension as com.google.protobuf.ExtensionLite<$message$, " - "kotlin.collections.List<*>>) as T\n" - " } else {\n" - " _builder.getExtension(extension)\n" - " }\n" - "}\n\n", - "message", message_name); - - printer->Print( - "@kotlin.jvm.JvmSynthetic\n" - "@kotlin.OptIn" - "(com.google.protobuf.kotlin.OnlyForUseByGeneratedProtoCode::class)\n" - "@kotlin.jvm.JvmName(\"-getRepeatedExtension\")\n" - "public operator fun get(\n" - " extension: com.google.protobuf.ExtensionLite<$message$, " - "kotlin.collections.List>\n" - "): com.google.protobuf.kotlin.ExtensionList {\n" - " return com.google.protobuf.kotlin.ExtensionList(extension, " - "_builder.getExtension(extension))\n" - "}\n\n", - "message", message_name); - - printer->Print( - "@kotlin.jvm.JvmSynthetic\n" - "public operator fun contains(extension: " - "com.google.protobuf.ExtensionLite<$message$, *>): " - "Boolean {\n" - " return _builder.hasExtension(extension)\n" - "}\n\n", - "message", message_name); - - printer->Print( - "@kotlin.jvm.JvmSynthetic\n" - "public fun clear(extension: " - "com.google.protobuf.ExtensionLite<$message$, *>) " - "{\n" - " _builder.clearExtension(extension)\n" - "}\n\n", - "message", message_name); - - printer->Print( - "@kotlin.jvm.JvmSynthetic\n" - "public fun setExtension(extension: " - "com.google.protobuf.ExtensionLite<$message$, T>, " - "value: T) {\n" - " _builder.setExtension(extension, value)\n" - "}\n\n", - "message", message_name); - - printer->Print( - "@kotlin.jvm.JvmSynthetic\n" - "@Suppress(\"NOTHING_TO_INLINE\")\n" - "public inline operator fun > set(\n" - " extension: com.google.protobuf.ExtensionLite<$message$, T>,\n" - " value: T\n" - ") {\n" - " setExtension(extension, value)\n" - "}\n\n", - "message", message_name); - - printer->Print( - "@kotlin.jvm.JvmSynthetic\n" - "@Suppress(\"NOTHING_TO_INLINE\")\n" - "public inline operator fun set(\n" - " extension: com.google.protobuf.ExtensionLite<$message$, " - "com.google.protobuf.ByteString>,\n" - " value: com.google.protobuf.ByteString\n" - ") {\n" - " setExtension(extension, value)\n" - "}\n\n", - "message", message_name); - - printer->Print( - "@kotlin.jvm.JvmSynthetic\n" - "@Suppress(\"NOTHING_TO_INLINE\")\n" - "public inline operator fun set(\n" - " extension: com.google.protobuf.ExtensionLite<$message$, T>,\n" - " value: T\n" - ") {\n" - " setExtension(extension, value)\n" - "}\n\n", - "message", message_name); - - printer->Print( - "@kotlin.jvm.JvmSynthetic\n" - "public fun com.google.protobuf.kotlin.ExtensionList.add(value: E) {\n" - " _builder.addExtension(this.extension, value)\n" - "}\n\n", - "message", message_name); - - printer->Print( - "@kotlin.jvm.JvmSynthetic\n" - "@Suppress(\"NOTHING_TO_INLINE\")\n" - "public inline operator fun " - "com.google.protobuf.kotlin.ExtensionList.plusAssign" - "(value: E) {\n" - " add(value)\n" - "}\n\n", - "message", message_name); - - printer->Print( - "@kotlin.jvm.JvmSynthetic\n" - "public fun com.google.protobuf.kotlin.ExtensionList.addAll(values: Iterable) {\n" - " for (value in values) {\n" - " add(value)\n" - " }\n" - "}\n\n", - "message", message_name); - - printer->Print( - "@kotlin.jvm.JvmSynthetic\n" - "@Suppress(\"NOTHING_TO_INLINE\")\n" - "public inline operator fun " - "com.google.protobuf.kotlin.ExtensionList.plusAssign(values: " - "Iterable) {\n" - " addAll(values)\n" - "}\n\n", - "message", message_name); - - printer->Print( - "@kotlin.jvm.JvmSynthetic\n" - "public operator fun " - "com.google.protobuf.kotlin.ExtensionList.set(index: Int, value: " - "E) {\n" - " _builder.setExtension(this.extension, index, value)\n" - "}\n\n", - "message", message_name); - - printer->Print( - "@kotlin.jvm.JvmSynthetic\n" - "@Suppress(\"NOTHING_TO_INLINE\")\n" - "public inline fun com.google.protobuf.kotlin.ExtensionList<*, " - "$message$>.clear() {\n" - " clear(extension)\n" - "}\n\n", - "message", message_name); -} - void ImmutableMessageGenerator::GenerateAnyMethods(io::Printer* printer) { printer->Print( "private static String getTypeUrl(\n" diff --git a/src/google/protobuf/compiler/java/full/message.h b/src/google/protobuf/compiler/java/full/message.h index 3b929b8d3f..977bcf1cf0 100644 --- a/src/google/protobuf/compiler/java/full/message.h +++ b/src/google/protobuf/compiler/java/full/message.h @@ -55,9 +55,6 @@ class ImmutableMessageGenerator : public MessageGenerator { // Returns an estimate of the number of bytes the printed code will compile to int GenerateStaticVariableInitializers(io::Printer* printer) override; - void GenerateKotlinDsl(io::Printer* printer) const override; - void GenerateKotlinMembers(io::Printer* printer) const override; - void GenerateTopLevelKotlinMembers(io::Printer* printer) const override; private: void GenerateFieldAccessorTable(io::Printer* printer, int* bytecode_estimate); @@ -76,8 +73,6 @@ class ImmutableMessageGenerator : public MessageGenerator { void GenerateParser(io::Printer* printer); void GenerateParsingConstructor(io::Printer* printer); void GenerateMutableCopy(io::Printer* printer); - void GenerateKotlinExtensions(io::Printer* printer) const; - void GenerateKotlinOrNull(io::Printer* printer) const; void GenerateAnyMethods(io::Printer* printer); Context* context_; diff --git a/src/google/protobuf/compiler/java/generator_common.h b/src/google/protobuf/compiler/java/generator_common.h index c2c8859d0f..33843a0597 100644 --- a/src/google/protobuf/compiler/java/generator_common.h +++ b/src/google/protobuf/compiler/java/generator_common.h @@ -23,6 +23,7 @@ class FieldGenerator { public: virtual ~FieldGenerator() = default; virtual void GenerateSerializationCode(io::Printer* printer) const = 0; + virtual void GenerateKotlinDslMembers(io::Printer* printer) const = 0; }; // Convenience class which constructs FieldGenerators for a Descriptor. diff --git a/src/google/protobuf/compiler/java/generator_factory.h b/src/google/protobuf/compiler/java/generator_factory.h index 2dcfad9517..40605c6e09 100644 --- a/src/google/protobuf/compiler/java/generator_factory.h +++ b/src/google/protobuf/compiler/java/generator_factory.h @@ -51,9 +51,6 @@ class MessageGenerator { // Generate code to register all contained extensions with an // ExtensionRegistry. virtual void GenerateExtensionRegistrationCode(io::Printer* printer) = 0; - virtual void GenerateKotlinDsl(io::Printer* printer) const = 0; - virtual void GenerateKotlinMembers(io::Printer* printer) const = 0; - virtual void GenerateTopLevelKotlinMembers(io::Printer* printer) const = 0; protected: const Descriptor* descriptor_; diff --git a/src/google/protobuf/compiler/java/helpers.h b/src/google/protobuf/compiler/java/helpers.h index fa359f9120..7e002f1190 100644 --- a/src/google/protobuf/compiler/java/helpers.h +++ b/src/google/protobuf/compiler/java/helpers.h @@ -380,17 +380,18 @@ const FieldDescriptor* MapKeyField(const FieldDescriptor* descriptor); const FieldDescriptor* MapValueField(const FieldDescriptor* descriptor); -inline std::string JvmSynthetic(const Options& options) { - return options.jvm_dsl ? "@kotlin.jvm.JvmSynthetic\n" : ""; +inline std::string JvmSynthetic(bool jvm_dsl) { + return jvm_dsl ? "@kotlin.jvm.JvmSynthetic\n" : ""; } struct JvmNameContext { const Options& options; io::Printer* printer; + bool lite = true; }; inline void JvmName(absl::string_view name, const JvmNameContext& context) { - if (!context.options.jvm_dsl) return; + if (context.lite && !context.options.jvm_dsl) return; context.printer->Emit("@kotlin.jvm.JvmName(\""); // Note: `name` will likely have vars in it that we do want to interpolate. context.printer->Emit(name); diff --git a/src/google/protobuf/compiler/java/lite/BUILD.bazel b/src/google/protobuf/compiler/java/lite/BUILD.bazel index 8171f5af14..10467390f6 100644 --- a/src/google/protobuf/compiler/java/lite/BUILD.bazel +++ b/src/google/protobuf/compiler/java/lite/BUILD.bazel @@ -24,7 +24,10 @@ cc_library( "string_field.h", ], strip_include_prefix = "/src", - visibility = ["//src/google/protobuf/compiler/java:__subpackages__"], + visibility = [ + "//src/google/protobuf/compiler/java:__subpackages__", + "//src/google/protobuf/compiler/kotlin:__subpackages__", + ], deps = [ "//src/google/protobuf", "//src/google/protobuf:port", diff --git a/src/google/protobuf/compiler/java/lite/field_generator.h b/src/google/protobuf/compiler/java/lite/field_generator.h index 7e34f11323..de44156683 100644 --- a/src/google/protobuf/compiler/java/lite/field_generator.h +++ b/src/google/protobuf/compiler/java/lite/field_generator.h @@ -1,8 +1,10 @@ -#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_IMMUTABLE_FIELD_GENERATOR_H__ -#define GOOGLE_PROTOBUF_COMPILER_JAVA_IMMUTABLE_FIELD_GENERATOR_H__ +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_LITE_FIELD_GENERATOR_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_LITE_FIELD_GENERATOR_H__ #include +#include "absl/log/absl_log.h" +#include "google/protobuf/compiler/java/generator_common.h" #include "google/protobuf/io/printer.h" namespace google { @@ -10,7 +12,7 @@ namespace protobuf { namespace compiler { namespace java { -class ImmutableFieldLiteGenerator { +class ImmutableFieldLiteGenerator : public FieldGenerator { public: ImmutableFieldLiteGenerator() = default; ImmutableFieldLiteGenerator(const ImmutableFieldLiteGenerator&) = delete; @@ -25,7 +27,10 @@ class ImmutableFieldLiteGenerator { virtual void GenerateInitializationCode(io::Printer* printer) const = 0; virtual void GenerateFieldInfo(io::Printer* printer, std::vector* output) const = 0; - virtual void GenerateKotlinDslMembers(io::Printer* printer) const = 0; + + void GenerateSerializationCode(io::Printer* printer) const override { + ABSL_LOG(FATAL) << "GenerateSerializationCode is unsupported for lite."; + } virtual std::string GetBoxedType() const = 0; }; @@ -35,4 +40,4 @@ class ImmutableFieldLiteGenerator { } // namespace protobuf } // namespace google -#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_IMMUTABLE_FIELD_GENERATOR_H__ +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_LITE_FIELD_GENERATOR_H__ diff --git a/src/google/protobuf/compiler/java/lite/message.cc b/src/google/protobuf/compiler/java/lite/message.cc index 2e1ecd38a0..79c3c3e642 100644 --- a/src/google/protobuf/compiler/java/lite/message.cc +++ b/src/google/protobuf/compiler/java/lite/message.cc @@ -743,375 +743,6 @@ void ImmutableMessageLiteGenerator::GenerateInitializers(io::Printer* printer) { } } } - -void ImmutableMessageLiteGenerator::GenerateKotlinDsl( - io::Printer* printer) const { - printer->Print( - "@kotlin.OptIn" - "(com.google.protobuf.kotlin.OnlyForUseByGeneratedProtoCode::class)\n" - "@com.google.protobuf.kotlin.ProtoDslMarker\n"); - printer->Print( - "public class Dsl private constructor(\n" - " private val _builder: $message$.Builder\n" - ") {\n" - " public companion object {\n" - "$ jvm_synthetic$" - " @kotlin.PublishedApi\n" - " internal fun _create(builder: $message$.Builder): Dsl = " - "Dsl(builder)\n" - " }\n" - "\n" - "$ jvm_synthetic$" - " @kotlin.PublishedApi\n" - " internal fun _build(): $message$ = _builder.build()\n", - "jvm_synthetic", JvmSynthetic(context_->options()), "message", - EscapeKotlinKeywords(name_resolver_->GetClassName(descriptor_, true))); - - printer->Indent(); - - for (int i = 0; i < descriptor_->field_count(); i++) { - printer->Print("\n"); - field_generators_.get(descriptor_->field(i)) - .GenerateKotlinDslMembers(printer); - } - - for (auto& kv : oneofs_) { - JvmNameContext name_ctx = {context_->options(), printer}; - const OneofDescriptor* oneof = kv.second; - auto oneof_name = context_->GetOneofGeneratorInfo(oneof)->name; - printer->Emit( - { - {"jvm_name", - [&] { JvmName("get$oneof_capitalized_name$Case", name_ctx); }}, - {"oneof_name", oneof_name}, - {"oneof_capitalized_name", - context_->GetOneofGeneratorInfo(oneof)->capitalized_name}, - {"oneof_property_name", GetKotlinPropertyName(oneof_name)}, - {"message", EscapeKotlinKeywords( - name_resolver_->GetClassName(descriptor_, true))}, - }, - "public val $oneof_name$Case: $message$.$oneof_capitalized_name$Case\n" - "$jvm_name$" - " get() = _builder.$oneof_property_name$Case\n\n" - "public fun clear$oneof_capitalized_name$() {\n" - " _builder.clear$oneof_capitalized_name$()\n" - "}\n"); - } - - if (descriptor_->extension_range_count() > 0) { - GenerateKotlinExtensions(printer); - } - - printer->Outdent(); - printer->Print("}\n"); -} - -void ImmutableMessageLiteGenerator::GenerateKotlinMembers( - io::Printer* printer) const { - if (context_->options().jvm_dsl) { - printer->Print("@kotlin.jvm.JvmName(\"-initialize$camelcase_name$\")\n", - "camelcase_name", - name_resolver_->GetKotlinFactoryName(descriptor_)); - } - - printer->Print( - "public inline fun $camelcase_name$(block: $message_kt$.Dsl.() -> " - "kotlin.Unit): $message$ =\n" - " $message_kt$.Dsl._create($message$.newBuilder()).apply { block() " - "}._build()\n", - "camelcase_name", name_resolver_->GetKotlinFactoryName(descriptor_), - "message_kt", - EscapeKotlinKeywords( - name_resolver_->GetKotlinExtensionsClassName(descriptor_)), - "message", - EscapeKotlinKeywords(name_resolver_->GetClassName(descriptor_, true))); - - WriteMessageDocComment(printer, descriptor_, context_->options(), - /* kdoc */ true); - printer->Print("public object $name$Kt {\n", "name", descriptor_->name()); - printer->Indent(); - GenerateKotlinDsl(printer); - for (int i = 0; i < descriptor_->nested_type_count(); i++) { - if (IsMapEntry(descriptor_->nested_type(i))) continue; - ImmutableMessageLiteGenerator(descriptor_->nested_type(i), context_) - .GenerateKotlinMembers(printer); - } - printer->Outdent(); - printer->Print("}\n"); -} - -void ImmutableMessageLiteGenerator::GenerateTopLevelKotlinMembers( - io::Printer* printer) const { - printer->Print( - "public inline fun $message$.copy(block: $message_kt$.Dsl.() -> " - "kotlin.Unit): $message$ =\n" - " $message_kt$.Dsl._create(this.toBuilder()).apply { block() " - "}._build()\n\n", - "message", - EscapeKotlinKeywords(name_resolver_->GetClassName(descriptor_, true)), - "message_kt", - name_resolver_->GetKotlinExtensionsClassNameEscaped(descriptor_)); - - for (int i = 0; i < descriptor_->nested_type_count(); i++) { - if (IsMapEntry(descriptor_->nested_type(i))) continue; - ImmutableMessageLiteGenerator(descriptor_->nested_type(i), context_) - .GenerateTopLevelKotlinMembers(printer); - } - - GenerateKotlinOrNull(printer); -} - -void ImmutableMessageLiteGenerator::GenerateKotlinOrNull( - io::Printer* printer) const { - for (int i = 0; i < descriptor_->field_count(); i++) { - const FieldDescriptor* field = descriptor_->field(i); - if (!field->has_presence() || GetJavaType(field) != JAVATYPE_MESSAGE) { - continue; - } - if (context_->options().jvm_dsl) { - // On the JVM, we can use `FooOrBuilder`, and it saves code size to - // generate only one method instead of two. - if (field->options().deprecated()) { - printer->Print( - "@kotlin.Deprecated(message = \"Field $name$ is deprecated\")\n", - "name", context_->GetFieldGeneratorInfo(field)->name); - } - printer->Print( - "public val $full_classname$OrBuilder.$camelcase_name$OrNull: " - "$full_name$?\n" - " get() = if (has$name$()) get$name$() else null\n\n", - "full_classname", - EscapeKotlinKeywords(name_resolver_->GetClassName(descriptor_, true)), - "camelcase_name", context_->GetFieldGeneratorInfo(field)->name, - "full_name", - EscapeKotlinKeywords( - name_resolver_->GetImmutableClassName(field->message_type())), - "name", context_->GetFieldGeneratorInfo(field)->capitalized_name); - } else { - // Non-JVM platforms don't have `FooOrBuilder`, so we generate `Foo` - // and `Foo.Builder` methods. - if (field->options().deprecated()) { - printer->Print( - "@kotlin.Deprecated(message = \"Field $name$ is deprecated\")\n", - "name", context_->GetFieldGeneratorInfo(field)->name); - } - printer->Print( - "public val $full_classname$.$camelcase_name$OrNull: " - "$full_name$?\n" - " get() = if (has$capitalized_name$()) this.$name$ else null\n\n", - "full_classname", - EscapeKotlinKeywords(name_resolver_->GetClassName(descriptor_, true)), - "camelcase_name", context_->GetFieldGeneratorInfo(field)->name, - "full_name", - EscapeKotlinKeywords( - name_resolver_->GetImmutableClassName(field->message_type())), - "capitalized_name", - context_->GetFieldGeneratorInfo(field)->capitalized_name, "name", - EscapeKotlinKeywords(GetKotlinPropertyName( - context_->GetFieldGeneratorInfo(field)->capitalized_name))); - if (field->options().deprecated()) { - printer->Print( - "@kotlin.Deprecated(message = \"Field $name$ is deprecated\")\n", - "name", context_->GetFieldGeneratorInfo(field)->name); - } - printer->Print( - "public val $full_classname$.Builder.$camelcase_name$OrNull: " - "$full_name$?\n" - " get() = if (has$capitalized_name$()) this.$name$ else null\n\n", - "full_classname", - EscapeKotlinKeywords(name_resolver_->GetClassName(descriptor_, true)), - "camelcase_name", context_->GetFieldGeneratorInfo(field)->name, - "full_name", - EscapeKotlinKeywords( - name_resolver_->GetImmutableClassName(field->message_type())), - "capitalized_name", - context_->GetFieldGeneratorInfo(field)->capitalized_name, "name", - EscapeKotlinKeywords(GetKotlinPropertyName( - context_->GetFieldGeneratorInfo(field)->capitalized_name))); - } - } -} - -void ImmutableMessageLiteGenerator::GenerateKotlinExtensions( - io::Printer* printer) const { - JvmNameContext name_ctx = {context_->options(), printer}; - std::string message_name = - EscapeKotlinKeywords(name_resolver_->GetClassName(descriptor_, true)); - - printer->Print( - "@Suppress(\"UNCHECKED_CAST\")\n" - "$jvm_synthetic$" - "public operator fun get(extension: " - "com.google.protobuf.ExtensionLite<$message$, T>): T {\n" - " return if (extension.isRepeated) {\n" - " get(extension as com.google.protobuf.ExtensionLite<$message$, " - "kotlin.collections.List<*>>) as T\n" - " } else {\n" - " _builder.getExtension(extension)\n" - " }\n" - "}\n\n", - "jvm_synthetic", JvmSynthetic(context_->options()), "message", - message_name); - - if (context_->options().jvm_dsl) { - // TODO: generate this on Kotlin Native once the [Mutable]List - // issue is resolved. - printer->Emit( - { - {"jvm_name", [&] { JvmName("-getRepeatedExtension", name_ctx); }}, - {"jvm_synthetic", JvmSynthetic(context_->options())}, - {"message", message_name}, - }, - "$jvm_synthetic$" - "@kotlin.OptIn" - "(com.google.protobuf.kotlin.OnlyForUseByGeneratedProtoCode::class)\n" - "$jvm_name$" - "public operator fun get(\n" - " extension: com.google.protobuf.ExtensionLite<$message$, " - "kotlin.collections.List>\n" - "): com.google.protobuf.kotlin.ExtensionList {\n" - " return com.google.protobuf.kotlin.ExtensionList(extension, " - "_builder.getExtension(extension))\n" - "}\n\n"); - } - - printer->Print( - "$jvm_synthetic$" - "public operator fun contains(extension: " - "com.google.protobuf.ExtensionLite<$message$, *>): " - "Boolean {\n" - " return _builder.hasExtension(extension)\n" - "}\n\n", - "jvm_synthetic", JvmSynthetic(context_->options()), "message", - message_name); - - printer->Print( - "$jvm_synthetic$" - "public fun clear(extension: " - "com.google.protobuf.ExtensionLite<$message$, *>) " - "{\n" - " _builder.clearExtension(extension)\n" - "}\n\n", - "jvm_synthetic", JvmSynthetic(context_->options()), "message", - message_name); - - printer->Print( - "$jvm_synthetic$" - "public fun setExtension(extension: " - "com.google.protobuf.ExtensionLite<$message$, T>, " - "value: T) {\n" - " _builder.setExtension(extension, value)\n" - "}\n\n", - "jvm_synthetic", JvmSynthetic(context_->options()), "message", - message_name); - - printer->Print( - "$jvm_synthetic$" - "@Suppress(\"NOTHING_TO_INLINE\")\n" - "public inline operator fun > set(\n" - " extension: com.google.protobuf.ExtensionLite<$message$, T>,\n" - " value: T\n" - ") {\n" - " setExtension(extension, value)\n" - "}\n\n", - "jvm_synthetic", JvmSynthetic(context_->options()), "message", - message_name); - - printer->Print( - "$jvm_synthetic$" - "@Suppress(\"NOTHING_TO_INLINE\")\n" - "public inline operator fun set(\n" - " extension: com.google.protobuf.ExtensionLite<$message$, " - "com.google.protobuf.ByteString>,\n" - " value: com.google.protobuf.ByteString\n" - ") {\n" - " setExtension(extension, value)\n" - "}\n\n", - "jvm_synthetic", JvmSynthetic(context_->options()), "message", - message_name); - - printer->Print( - "$jvm_synthetic$" - "@Suppress(\"NOTHING_TO_INLINE\")\n" - "public inline operator fun set(\n" - " extension: com.google.protobuf.ExtensionLite<$message$, T>,\n" - " value: T\n" - ") {\n" - " setExtension(extension, value)\n" - "}\n\n", - "jvm_synthetic", JvmSynthetic(context_->options()), "message", - message_name); - - // TODO: generate these methods on Kotlin Native once the - // [Mutable]List issue is resolved. - if (!context_->options().jvm_dsl) return; - - printer->Print( - "$jvm_synthetic$" - "public fun com.google.protobuf.kotlin.ExtensionList.add(value: E) {\n" - " _builder.addExtension(this.extension, value)\n" - "}\n\n", - "jvm_synthetic", JvmSynthetic(context_->options()), "message", - message_name); - - printer->Print( - "$jvm_synthetic$" - "@Suppress(\"NOTHING_TO_INLINE\")\n" - "public inline operator fun " - "com.google.protobuf.kotlin.ExtensionList.plusAssign" - "(value: E) {\n" - " add(value)\n" - "}\n\n", - "jvm_synthetic", JvmSynthetic(context_->options()), "message", - message_name); - - printer->Print( - "$jvm_synthetic$" - "public fun com.google.protobuf.kotlin.ExtensionList.addAll(values: Iterable) {\n" - " for (value in values) {\n" - " add(value)\n" - " }\n" - "}\n\n", - "jvm_synthetic", JvmSynthetic(context_->options()), "message", - message_name); - - printer->Print( - "$jvm_synthetic$" - "@Suppress(\"NOTHING_TO_INLINE\")\n" - "public inline operator fun " - "com.google.protobuf.kotlin.ExtensionList.plusAssign(values: " - "Iterable) {\n" - " addAll(values)\n" - "}\n\n", - "jvm_synthetic", JvmSynthetic(context_->options()), "message", - message_name); - - printer->Print( - "$jvm_synthetic$" - "public operator fun " - "com.google.protobuf.kotlin.ExtensionList.set(index: Int, value: " - "E) {\n" - " _builder.setExtension(this.extension, index, value)\n" - "}\n\n", - "jvm_synthetic", JvmSynthetic(context_->options()), "message", - message_name); - - printer->Print( - "$jvm_synthetic$" - "@Suppress(\"NOTHING_TO_INLINE\")\n" - "public inline fun com.google.protobuf.kotlin.ExtensionList<*, " - "$message$>.clear() {\n" - " clear(extension)\n" - "}\n\n", - "jvm_synthetic", JvmSynthetic(context_->options()), "message", - message_name); -} - } // namespace java } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/java/lite/message.h b/src/google/protobuf/compiler/java/lite/message.h index a20cb251ec..0aea5b9a2f 100644 --- a/src/google/protobuf/compiler/java/lite/message.h +++ b/src/google/protobuf/compiler/java/lite/message.h @@ -42,9 +42,6 @@ class ImmutableMessageLiteGenerator : public MessageGenerator { void GenerateStaticVariables(io::Printer* printer, int* bytecode_estimate) override; int GenerateStaticVariableInitializers(io::Printer* printer) override; - void GenerateKotlinDsl(io::Printer* printer) const override; - void GenerateKotlinMembers(io::Printer* printer) const override; - void GenerateTopLevelKotlinMembers(io::Printer* printer) const override; private: void GenerateParseFromMethods(io::Printer* printer); @@ -55,8 +52,6 @@ class ImmutableMessageLiteGenerator : public MessageGenerator { void GenerateParser(io::Printer* printer); void GenerateConstructor(io::Printer* printer); void GenerateDynamicMethodNewBuildMessageInfo(io::Printer* printer); - void GenerateKotlinExtensions(io::Printer* printer) const; - void GenerateKotlinOrNull(io::Printer* printer) const; Context* context_; ClassNameResolver* name_resolver_; diff --git a/src/google/protobuf/compiler/kotlin/BUILD.bazel b/src/google/protobuf/compiler/kotlin/BUILD.bazel index 2b361e7bb6..5555adef0f 100644 --- a/src/google/protobuf/compiler/kotlin/BUILD.bazel +++ b/src/google/protobuf/compiler/kotlin/BUILD.bazel @@ -21,8 +21,14 @@ cc_library( cc_library( name = "kotlin_internal", - srcs = ["file.cc"], - hdrs = ["file.h"], + srcs = [ + "file.cc", + "message.cc", + ], + hdrs = [ + "file.h", + "message.h", + ], strip_include_prefix = "/src", visibility = [ "//pkg:__pkg__", @@ -34,11 +40,15 @@ cc_library( "//src/google/protobuf:protobuf_lite", "//src/google/protobuf/compiler:code_generator", "//src/google/protobuf/compiler/java", + "//src/google/protobuf/compiler/java:generator_common", "//src/google/protobuf/compiler/java:helpers", - "//src/google/protobuf/compiler/java/full", - "//src/google/protobuf/compiler/java/lite", + "//src/google/protobuf/compiler/java/full:fg", + "//src/google/protobuf/compiler/java/full:mfg", + "//src/google/protobuf/compiler/java/lite:field_generators", "//src/google/protobuf/io", "//src/google/protobuf/io:printer", + "@com_google_absl//absl/container:btree", + "@com_google_absl//absl/log:absl_check", "@com_google_absl//absl/strings", ], ) diff --git a/src/google/protobuf/compiler/kotlin/file.cc b/src/google/protobuf/compiler/kotlin/file.cc index 6d586a8c11..8d475bde03 100644 --- a/src/google/protobuf/compiler/kotlin/file.cc +++ b/src/google/protobuf/compiler/kotlin/file.cc @@ -5,8 +5,6 @@ // license that can be found in the LICENSE file or at // https://developers.google.com/open-source/licenses/bsd -// Generates Kotlin code for a given .proto file. - #include "google/protobuf/compiler/kotlin/file.h" #include @@ -16,13 +14,11 @@ #include "absl/strings/str_cat.h" #include "google/protobuf/compiler/code_generator.h" #include "google/protobuf/compiler/java/context.h" -#include "google/protobuf/compiler/java/generator_factory.h" #include "google/protobuf/compiler/java/helpers.h" -#include "google/protobuf/compiler/java/full/generator_factory.h" -#include "google/protobuf/compiler/java/lite/generator_factory.h" #include "google/protobuf/compiler/java/name_resolver.h" #include "google/protobuf/compiler/java/names.h" #include "google/protobuf/compiler/java/options.h" +#include "google/protobuf/compiler/kotlin/message.h" #include "google/protobuf/descriptor.pb.h" #include "google/protobuf/io/printer.h" @@ -34,30 +30,16 @@ namespace kotlin { using google::protobuf::compiler::java::Context; using google::protobuf::compiler::java::Options; -// class LiteGeneratorFactory; - -namespace { -std::unique_ptr CreateGeneratorFactory( - const FileDescriptor* file, const Options& options, Context* context) { - if (java::HasDescriptorMethods(file, context->EnforceLite())) { - return java::MakeImmutableGeneratorFactory(context); - } else { - return java::MakeImmutableLiteGeneratorFactory(context); - } -} -} // namespace - FileGenerator::FileGenerator(const FileDescriptor* file, const Options& options) : file_(file), java_package_(java::FileJavaPackage(file, options)), message_generators_(file->message_type_count()), context_(new Context(file, options)), - generator_factory_(CreateGeneratorFactory(file, options, context_.get())), name_resolver_(context_->GetNameResolver()), options_(options) { for (int i = 0; i < file_->message_type_count(); ++i) { - message_generators_[i] = - generator_factory_->NewMessageGenerator(file_->message_type(i)); + message_generators_[i] = std::make_unique( + file_->message_type(i), context_.get()); } } @@ -91,7 +73,7 @@ void FileGenerator::GenerateSiblings( std::vector* annotation_list) { for (int i = 0; i < file_->message_type_count(); i++) { const Descriptor* descriptor = file_->message_type(i); - java::MessageGenerator* generator = message_generators_[i].get(); + MessageGenerator* generator = message_generators_[i].get(); auto open_file = [context](const std::string& filename) { return std::unique_ptr(context->Open(filename)); }; @@ -125,8 +107,8 @@ void FileGenerator::GenerateSiblings( "package", java::EscapeKotlinKeywords(java_package_)); } - generator->GenerateKotlinMembers(&printer); - generator->GenerateTopLevelKotlinMembers(&printer); + generator->GenerateMembers(&printer); + generator->GenerateTopLevelMembers(&printer); if (options_.annotate_code) { auto info_output = open_file(info_full_path); diff --git a/src/google/protobuf/compiler/kotlin/file.h b/src/google/protobuf/compiler/kotlin/file.h index 4c5fbf76ae..854f15add6 100644 --- a/src/google/protobuf/compiler/kotlin/file.h +++ b/src/google/protobuf/compiler/kotlin/file.h @@ -14,8 +14,8 @@ #include #include -#include "google/protobuf/compiler/java/generator_factory.h" #include "google/protobuf/compiler/java/options.h" +#include "google/protobuf/compiler/kotlin/message.h" namespace google { namespace protobuf { @@ -64,9 +64,8 @@ class FileGenerator { const FileDescriptor* file_; std::string java_package_; - std::vector> message_generators_; + std::vector> message_generators_; std::unique_ptr context_; - std::unique_ptr generator_factory_; java::ClassNameResolver* name_resolver_; const java::Options options_; }; diff --git a/src/google/protobuf/compiler/kotlin/message.cc b/src/google/protobuf/compiler/kotlin/message.cc new file mode 100644 index 0000000000..e64a0987e6 --- /dev/null +++ b/src/google/protobuf/compiler/kotlin/message.cc @@ -0,0 +1,444 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +#include "google/protobuf/compiler/kotlin/message.h" + +#include + +#include "absl/log/absl_check.h" +#include "absl/strings/str_cat.h" +#include "google/protobuf/compiler/java/context.h" +#include "google/protobuf/compiler/java/doc_comment.h" +#include "google/protobuf/compiler/java/field_common.h" +#include "google/protobuf/compiler/java/generator_common.h" +#include "google/protobuf/compiler/java/helpers.h" +#include "google/protobuf/compiler/java/full/field_generator.h" +#include "google/protobuf/compiler/java/full/make_field_gens.h" +#include "google/protobuf/compiler/java/lite/field_generator.h" +#include "google/protobuf/compiler/java/lite/make_field_gens.h" +#include "google/protobuf/compiler/java/name_resolver.h" +#include "google/protobuf/io/printer.h" + +// Must be last. +#include "google/protobuf/port_def.inc" + +namespace google { +namespace protobuf { +namespace compiler { +namespace kotlin { + +MessageGenerator::MessageGenerator(const Descriptor* descriptor, + java::Context* context) + : context_(context), + name_resolver_(context->GetNameResolver()), + descriptor_(descriptor), + lite_(!java::HasDescriptorMethods(descriptor_->file(), + context->EnforceLite())), + jvm_dsl_(!lite_ || context->options().jvm_dsl), + lite_field_generators_( + java::FieldGeneratorMap( + descriptor_)), + field_generators_( + java::FieldGeneratorMap(descriptor_)) { + for (int i = 0; i < descriptor_->field_count(); i++) { + if (java::IsRealOneof(descriptor_->field(i))) { + const OneofDescriptor* oneof = descriptor_->field(i)->containing_oneof(); + ABSL_CHECK(oneofs_.emplace(oneof->index(), oneof).first->second == oneof); + } + } + if (lite_) { + lite_field_generators_ = + java::MakeImmutableFieldLiteGenerators(descriptor_, context_); + } else { + field_generators_ = + java::MakeImmutableFieldGenerators(descriptor_, context_); + } +} + +void MessageGenerator::GenerateFieldMembers(io::Printer* printer) const { + for (int i = 0; i < descriptor_->field_count(); i++) { + printer->Print("\n"); + if (lite_) { + lite_field_generators_.get(descriptor_->field(i)) + .GenerateKotlinDslMembers(printer); + } else { + field_generators_.get(descriptor_->field(i)) + .GenerateKotlinDslMembers(printer); + } + } +} + +void MessageGenerator::Generate(io::Printer* printer) const { + printer->Print( + "@kotlin.OptIn" + "(com.google.protobuf.kotlin.OnlyForUseByGeneratedProtoCode::class)\n" + "@com.google.protobuf.kotlin.ProtoDslMarker\n"); + printer->Print( + "public class Dsl private constructor(\n" + " private val _builder: $message$.Builder\n" + ") {\n" + " public companion object {\n" + " $jvm_synthetic$" + " @kotlin.PublishedApi\n" + " internal fun _create(builder: $message$.Builder): Dsl = " + "Dsl(builder)\n" + " }\n" + "\n" + " $jvm_synthetic$" + " @kotlin.PublishedApi\n" + " internal fun _build(): $message$ = _builder.build()\n", + "jvm_synthetic", java::JvmSynthetic(jvm_dsl_), "message", + java::EscapeKotlinKeywords( + name_resolver_->GetClassName(descriptor_, true))); + + printer->Indent(); + + GenerateFieldMembers(printer); + + for (auto& kv : oneofs_) { + java::JvmNameContext name_ctx = {context_->options(), printer, lite_}; + const OneofDescriptor* oneof = kv.second; + auto oneof_name = context_->GetOneofGeneratorInfo(oneof)->name; + std::string oneof_capitalized_name = + context_->GetOneofGeneratorInfo(oneof)->capitalized_name; + std::string oneof_case_getter = + lite_ ? absl::StrCat(java::GetKotlinPropertyName(oneof_name), "Case") + : absl::StrCat("get", oneof_capitalized_name, "Case()"); + printer->Emit( + { + {"jvm_name", + [&] { + java::JvmName("get$oneof_capitalized_name$Case", name_ctx); + }}, + {"oneof_name", oneof_name}, + {"oneof_capitalized_name", oneof_capitalized_name}, + {"oneof_case_getter", oneof_case_getter}, + {"message", java::EscapeKotlinKeywords( + name_resolver_->GetClassName(descriptor_, true))}, + }, + "public val $oneof_name$Case: $message$.$oneof_capitalized_name$Case\n" + "$jvm_name$" + " get() = _builder.$oneof_case_getter$\n\n" + "public fun clear$oneof_capitalized_name$() {\n" + " _builder.clear$oneof_capitalized_name$()\n" + "}\n"); + } + + if (descriptor_->extension_range_count() > 0) { + GenerateExtensions(printer); + } + + printer->Outdent(); + printer->Print("}\n"); +} + +void MessageGenerator::GenerateMembers(io::Printer* printer) const { + if (jvm_dsl_) { + printer->Print("@kotlin.jvm.JvmName(\"-initialize$camelcase_name$\")\n", + "camelcase_name", + name_resolver_->GetKotlinFactoryName(descriptor_)); + } + + printer->Print( + "public inline fun $camelcase_name$(block: $message_kt$.Dsl.() -> " + "kotlin.Unit): $message$ =\n" + " $message_kt$.Dsl._create($message$.newBuilder()).apply { block() " + "}._build()\n", + "camelcase_name", name_resolver_->GetKotlinFactoryName(descriptor_), + "message_kt", + java::EscapeKotlinKeywords( + name_resolver_->GetKotlinExtensionsClassName(descriptor_)), + "message", + java::EscapeKotlinKeywords( + name_resolver_->GetClassName(descriptor_, true))); + + WriteMessageDocComment(printer, descriptor_, context_->options(), + /* kdoc */ true); + printer->Emit( + { + io::Printer::Sub{"name_kt", absl::StrCat(descriptor_->name(), "Kt")} + .AnnotatedAs(descriptor_), + {"body", + [&]() { + Generate(printer); + for (int i = 0; i < descriptor_->nested_type_count(); i++) { + if (java::IsMapEntry(descriptor_->nested_type(i))) continue; + MessageGenerator(descriptor_->nested_type(i), context_) + .GenerateMembers(printer); + } + }}, + }, + R"kt( + public object $name_kt$ { + $body$; + } + )kt"); +} + +void MessageGenerator::GenerateTopLevelMembers(io::Printer* printer) const { + if (!lite_) printer->Print("@kotlin.jvm.JvmSynthetic\n"); + printer->Print( + "public inline fun $message$.copy(block: $message_kt$.Dsl.() -> " + "kotlin.Unit): $message$ =\n" + " $message_kt$.Dsl._create(this.toBuilder()).apply { block() " + "}._build()\n\n", + "message", + java::EscapeKotlinKeywords( + name_resolver_->GetClassName(descriptor_, true)), + "message_kt", + name_resolver_->GetKotlinExtensionsClassNameEscaped(descriptor_)); + + for (int i = 0; i < descriptor_->nested_type_count(); i++) { + if (java::IsMapEntry(descriptor_->nested_type(i))) continue; + MessageGenerator(descriptor_->nested_type(i), context_) + .GenerateTopLevelMembers(printer); + } + + GenerateOrNull(printer); +} + +void MessageGenerator::GenerateOrNull(io::Printer* printer) const { + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + if (!field->has_presence() || + java::GetJavaType(field) != java::JAVATYPE_MESSAGE) { + continue; + } + if (field->options().deprecated()) { + printer->Print( + "@kotlin.Deprecated(message = \"Field $name$ is deprecated\")\n", + "name", context_->GetFieldGeneratorInfo(field)->name); + } + if (jvm_dsl_) { + // On the JVM, we can use `FooOrBuilder`, and it saves code size to + // generate only one method instead of two. + printer->Print( + "public val $full_classname$OrBuilder.$camelcase_name$OrNull: " + "$full_name$?\n" + " get() = if (has$name$()) get$name$() else null\n\n", + "full_classname", + java::EscapeKotlinKeywords( + name_resolver_->GetClassName(descriptor_, true)), + "camelcase_name", context_->GetFieldGeneratorInfo(field)->name, + "full_name", + java::EscapeKotlinKeywords( + name_resolver_->GetImmutableClassName(field->message_type())), + "name", context_->GetFieldGeneratorInfo(field)->capitalized_name); + } else { + // Non-JVM platforms don't have `FooOrBuilder`, so we generate `Foo` + // and `Foo.Builder` methods. + printer->Print( + "public val $full_classname$.$camelcase_name$OrNull: " + "$full_name$?\n" + " get() = if (has$capitalized_name$()) this.$name$ else null\n\n", + "full_classname", + java::EscapeKotlinKeywords( + name_resolver_->GetClassName(descriptor_, true)), + "camelcase_name", context_->GetFieldGeneratorInfo(field)->name, + "full_name", + java::EscapeKotlinKeywords( + name_resolver_->GetImmutableClassName(field->message_type())), + "capitalized_name", + context_->GetFieldGeneratorInfo(field)->capitalized_name, "name", + java::EscapeKotlinKeywords(java::GetKotlinPropertyName( + context_->GetFieldGeneratorInfo(field)->capitalized_name))); + if (field->options().deprecated()) { + printer->Print( + "@kotlin.Deprecated(message = \"Field $name$ is deprecated\")\n", + "name", context_->GetFieldGeneratorInfo(field)->name); + } + printer->Print( + "public val $full_classname$.Builder.$camelcase_name$OrNull: " + "$full_name$?\n" + " get() = if (has$capitalized_name$()) this.$name$ else null\n\n", + "full_classname", + java::EscapeKotlinKeywords( + name_resolver_->GetClassName(descriptor_, true)), + "camelcase_name", context_->GetFieldGeneratorInfo(field)->name, + "full_name", + java::EscapeKotlinKeywords( + name_resolver_->GetImmutableClassName(field->message_type())), + "capitalized_name", + context_->GetFieldGeneratorInfo(field)->capitalized_name, "name", + java::EscapeKotlinKeywords(java::GetKotlinPropertyName( + context_->GetFieldGeneratorInfo(field)->capitalized_name))); + } + } +} + +void MessageGenerator::GenerateExtensions(io::Printer* printer) const { + java::JvmNameContext name_ctx = {context_->options(), printer, lite_}; + std::string message_name = java::EscapeKotlinKeywords( + name_resolver_->GetClassName(descriptor_, true)); + + printer->Print( + "@Suppress(\"UNCHECKED_CAST\")\n" + "$jvm_synthetic$" + "public operator fun get(extension: " + "com.google.protobuf.ExtensionLite<$message$, T>): T {\n" + " return if (extension.isRepeated) {\n" + " get(extension as com.google.protobuf.ExtensionLite<$message$, " + "kotlin.collections.List<*>>) as T\n" + " } else {\n" + " _builder.getExtension(extension)\n" + " }\n" + "}\n\n", + "jvm_synthetic", java::JvmSynthetic(jvm_dsl_), "message", message_name); + + if (jvm_dsl_) { + // TODO: generate this on Kotlin Native once the [Mutable]List + // issue is resolved. + printer->Emit( + { + {"jvm_name", + [&] { java::JvmName("-getRepeatedExtension", name_ctx); }}, + {"jvm_synthetic", java::JvmSynthetic(jvm_dsl_)}, + {"message", message_name}, + }, + "$jvm_synthetic$" + "@kotlin.OptIn" + "(com.google.protobuf.kotlin.OnlyForUseByGeneratedProtoCode::class)\n" + "$jvm_name$" + "public operator fun get(\n" + " extension: com.google.protobuf.ExtensionLite<$message$, " + "kotlin.collections.List>\n" + "): com.google.protobuf.kotlin.ExtensionList {\n" + " return com.google.protobuf.kotlin.ExtensionList(extension, " + "_builder.getExtension(extension))\n" + "}\n\n"); + } + + printer->Print( + "$jvm_synthetic$" + "public operator fun contains(extension: " + "com.google.protobuf.ExtensionLite<$message$, *>): " + "Boolean {\n" + " return _builder.hasExtension(extension)\n" + "}\n\n", + "jvm_synthetic", java::JvmSynthetic(jvm_dsl_), "message", message_name); + + printer->Print( + "$jvm_synthetic$" + "public fun clear(extension: " + "com.google.protobuf.ExtensionLite<$message$, *>) " + "{\n" + " _builder.clearExtension(extension)\n" + "}\n\n", + "jvm_synthetic", java::JvmSynthetic(jvm_dsl_), "message", message_name); + + printer->Print( + "$jvm_synthetic$" + "public fun setExtension(extension: " + "com.google.protobuf.ExtensionLite<$message$, T>, " + "value: T) {\n" + " _builder.setExtension(extension, value)\n" + "}\n\n", + "jvm_synthetic", java::JvmSynthetic(jvm_dsl_), "message", message_name); + + printer->Print( + "$jvm_synthetic$" + "@Suppress(\"NOTHING_TO_INLINE\")\n" + "public inline operator fun > set(\n" + " extension: com.google.protobuf.ExtensionLite<$message$, T>,\n" + " value: T\n" + ") {\n" + " setExtension(extension, value)\n" + "}\n\n", + "jvm_synthetic", java::JvmSynthetic(jvm_dsl_), "message", message_name); + + printer->Print( + "$jvm_synthetic$" + "@Suppress(\"NOTHING_TO_INLINE\")\n" + "public inline operator fun set(\n" + " extension: com.google.protobuf.ExtensionLite<$message$, " + "com.google.protobuf.ByteString>,\n" + " value: com.google.protobuf.ByteString\n" + ") {\n" + " setExtension(extension, value)\n" + "}\n\n", + "jvm_synthetic", java::JvmSynthetic(jvm_dsl_), "message", message_name); + + printer->Print( + "$jvm_synthetic$" + "@Suppress(\"NOTHING_TO_INLINE\")\n" + "public inline operator fun set(\n" + " extension: com.google.protobuf.ExtensionLite<$message$, T>,\n" + " value: T\n" + ") {\n" + " setExtension(extension, value)\n" + "}\n\n", + "jvm_synthetic", java::JvmSynthetic(jvm_dsl_), "message", message_name); + + // TODO: generate these methods on Kotlin Native once the + // [Mutable]List issue is resolved. + if (!jvm_dsl_) return; + + printer->Print( + "$jvm_synthetic$" + "public fun com.google.protobuf.kotlin.ExtensionList.add(value: E) {\n" + " _builder.addExtension(this.extension, value)\n" + "}\n\n", + "jvm_synthetic", java::JvmSynthetic(jvm_dsl_), "message", message_name); + + printer->Print( + "$jvm_synthetic$" + "@Suppress(\"NOTHING_TO_INLINE\")\n" + "public inline operator fun " + "com.google.protobuf.kotlin.ExtensionList.plusAssign" + "(value: E) {\n" + " add(value)\n" + "}\n\n", + "jvm_synthetic", java::JvmSynthetic(jvm_dsl_), "message", message_name); + + printer->Print( + "$jvm_synthetic$" + "public fun com.google.protobuf.kotlin.ExtensionList.addAll(values: Iterable) {\n" + " for (value in values) {\n" + " add(value)\n" + " }\n" + "}\n\n", + "jvm_synthetic", java::JvmSynthetic(jvm_dsl_), "message", message_name); + + printer->Print( + "$jvm_synthetic$" + "@Suppress(\"NOTHING_TO_INLINE\")\n" + "public inline operator fun " + "com.google.protobuf.kotlin.ExtensionList.plusAssign(values: " + "Iterable) {\n" + " addAll(values)\n" + "}\n\n", + "jvm_synthetic", java::JvmSynthetic(jvm_dsl_), "message", message_name); + + printer->Print( + "$jvm_synthetic$" + "public operator fun " + "com.google.protobuf.kotlin.ExtensionList.set(index: Int, value: " + "E) {\n" + " _builder.setExtension(this.extension, index, value)\n" + "}\n\n", + "jvm_synthetic", java::JvmSynthetic(jvm_dsl_), "message", message_name); + + printer->Print( + "$jvm_synthetic$" + "@Suppress(\"NOTHING_TO_INLINE\")\n" + "public inline fun com.google.protobuf.kotlin.ExtensionList<*, " + "$message$>.clear() {\n" + " clear(extension)\n" + "}\n\n", + "jvm_synthetic", java::JvmSynthetic(jvm_dsl_), "message", message_name); +} +} // namespace kotlin +} // namespace compiler +} // namespace protobuf +} // namespace google + +#include "google/protobuf/port_undef.inc" diff --git a/src/google/protobuf/compiler/kotlin/message.h b/src/google/protobuf/compiler/kotlin/message.h new file mode 100644 index 0000000000..28bb428b72 --- /dev/null +++ b/src/google/protobuf/compiler/kotlin/message.h @@ -0,0 +1,58 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd + +#ifndef GOOGLE_PROTOBUF_COMPILER_KOTLIN_MESSAGE_H__ +#define GOOGLE_PROTOBUF_COMPILER_KOTLIN_MESSAGE_H__ + +#include "absl/container/btree_map.h" +#include "google/protobuf/compiler/java/context.h" +#include "google/protobuf/compiler/java/generator_common.h" +#include "google/protobuf/compiler/java/full/field_generator.h" +#include "google/protobuf/compiler/java/lite/field_generator.h" +#include "google/protobuf/compiler/java/name_resolver.h" +#include "google/protobuf/descriptor.h" + +namespace google { +namespace protobuf { +namespace compiler { +namespace kotlin { + +class MessageGenerator { + public: + MessageGenerator(const Descriptor* descriptor, java::Context* context); + MessageGenerator(const MessageGenerator&) = delete; + MessageGenerator& operator=(const MessageGenerator&) = delete; + virtual ~MessageGenerator() = default; + + void Generate(io::Printer* printer) const; + void GenerateMembers(io::Printer* printer) const; + void GenerateTopLevelMembers(io::Printer* printer) const; + + private: + java::Context* context_; + java::ClassNameResolver* name_resolver_; + const Descriptor* descriptor_; + absl::btree_map oneofs_; + bool lite_; + bool jvm_dsl_; + + // TODO: b/366047913 - These can be simplified once lite and full field + // generators are unified. + java::FieldGeneratorMap + lite_field_generators_; + java::FieldGeneratorMap field_generators_; + + void GenerateExtensions(io::Printer* printer) const; + void GenerateOrNull(io::Printer* printer) const; + void GenerateFieldMembers(io::Printer* printer) const; +}; +} // namespace kotlin +} // namespace compiler +} // namespace protobuf +} // namespace google + +#endif // GOOGLE_PROTOBUF_COMPILER_KOTLIN_MESSAGE_H__