Merge full and lite message generator into one while branching on if generating lite kotlin and/or jvm code. PiperOrigin-RevId: 680580214pull/18547/head
parent
ea69081412
commit
1c068db518
18 changed files with 550 additions and 725 deletions
@ -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 <string> |
||||
|
||||
#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<java::ImmutableFieldLiteGenerator>( |
||||
descriptor_)), |
||||
field_generators_( |
||||
java::FieldGeneratorMap<java::ImmutableFieldGenerator>(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 <T : kotlin.Any> 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 <E : kotlin.Any> get(\n" |
||||
" extension: com.google.protobuf.ExtensionLite<$message$, " |
||||
"kotlin.collections.List<E>>\n" |
||||
"): com.google.protobuf.kotlin.ExtensionList<E, $message$> {\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 <T : kotlin.Any> 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 <T : Comparable<T>> 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 <T : com.google.protobuf.MessageLite> 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 <E : kotlin.Any> com.google.protobuf.kotlin.ExtensionList<E, " |
||||
"$message$>.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 <E : kotlin.Any> " |
||||
"com.google.protobuf.kotlin.ExtensionList<E, " |
||||
"$message$>.plusAssign" |
||||
"(value: E) {\n" |
||||
" add(value)\n" |
||||
"}\n\n", |
||||
"jvm_synthetic", java::JvmSynthetic(jvm_dsl_), "message", message_name); |
||||
|
||||
printer->Print( |
||||
"$jvm_synthetic$" |
||||
"public fun <E : kotlin.Any> com.google.protobuf.kotlin.ExtensionList<E, " |
||||
"$message$>.addAll(values: Iterable<E>) {\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 <E : kotlin.Any> " |
||||
"com.google.protobuf.kotlin.ExtensionList<E, " |
||||
"$message$>.plusAssign(values: " |
||||
"Iterable<E>) {\n" |
||||
" addAll(values)\n" |
||||
"}\n\n", |
||||
"jvm_synthetic", java::JvmSynthetic(jvm_dsl_), "message", message_name); |
||||
|
||||
printer->Print( |
||||
"$jvm_synthetic$" |
||||
"public operator fun <E : kotlin.Any> " |
||||
"com.google.protobuf.kotlin.ExtensionList<E, " |
||||
"$message$>.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" |
@ -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<int, const OneofDescriptor*> oneofs_; |
||||
bool lite_; |
||||
bool jvm_dsl_; |
||||
|
||||
// TODO: b/366047913 - These can be simplified once lite and full field
|
||||
// generators are unified.
|
||||
java::FieldGeneratorMap<java::ImmutableFieldLiteGenerator> |
||||
lite_field_generators_; |
||||
java::FieldGeneratorMap<java::ImmutableFieldGenerator> 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__
|
Loading…
Reference in new issue