diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml index 46c5515e223..895b65a397e 100644 --- a/.github/workflows/codespell.yml +++ b/.github/workflows/codespell.yml @@ -12,5 +12,5 @@ jobs: - uses: codespell-project/actions-codespell@master with: check_filenames: true - skip: ./.git,./conformance/third_party,*.snk,*.pb,*.pb.cc,*.pb.h,./src/google/protobuf/testdata,./objectivec/Tests,./python/compatibility_tests/v2.5.0/tests/google/protobuf/internal,./.github/workflows/codespell.yml - ignore_words_list: "alow,alse,ba,cleare,copyable,cloneable,dedup,dur,errorprone,files',fo,fundementals,hel,importd,inout,leapyear,nd,nin,ois,ons,parseable,process',te,testof,ue,unparseable,wasn,wee,gae,keyserver,objext,od,OptIn" + skip: ./.git,./conformance/third_party,*.snk,*.pb,*.pb.cc,*.pb.h,./src/google/protobuf/testdata,./objectivec/Tests,./python/compatibility_tests/v2.5.0/tests/google/protobuf/internal,./github/workflows/codespell.yml + ignore_words_list: "alow,alse,ba,cleare,copyable,cloneable,dedup,dur,errorprone,files',fo,fundementals,hel,importd,inout,leapyear,nd,nin,ois,ons,parseable,process',te,testof,ue,unparseable,wasn,wee,gae,keyserver,objext,od,optin" diff --git a/BUILD b/BUILD index 07ee629a18a..46d284ec185 100644 --- a/BUILD +++ b/BUILD @@ -401,6 +401,7 @@ cc_library( "src/google/protobuf/compiler/java/java_generator.cc", "src/google/protobuf/compiler/java/java_generator_factory.cc", "src/google/protobuf/compiler/java/java_helpers.cc", + "src/google/protobuf/compiler/java/java_kotlin_generator.cc", "src/google/protobuf/compiler/java/java_map_field.cc", "src/google/protobuf/compiler/java/java_map_field_lite.cc", "src/google/protobuf/compiler/java/java_message.cc", diff --git a/Makefile.am b/Makefile.am index 915184213ce..3f94f85ddc0 100644 --- a/Makefile.am +++ b/Makefile.am @@ -530,6 +530,35 @@ java_EXTRA_DIST= java/core/src/test/proto/com/google/protobuf/test_check_utf8_size.proto \ java/core/src/test/proto/com/google/protobuf/test_custom_options.proto \ java/core/src/test/proto/com/google/protobuf/wrappers_test.proto \ + java/kotlin/generate-sources-build.xml \ + java/kotlin/generate-test-sources-build.xml \ + java/kotlin/pom.xml \ + java/kotlin/src/main/kotlin/com/google/protobuf/DslList.kt \ + java/kotlin/src/main/kotlin/com/google/protobuf/DslMap.kt \ + java/kotlin/src/main/kotlin/com/google/protobuf/DslProxy.kt \ + java/kotlin/src/main/kotlin/com/google/protobuf/ExtendableMessageExtensions.kt \ + java/kotlin/src/main/kotlin/com/google/protobuf/ExtendableMessageLiteExtensions.kt\ + java/kotlin/src/main/kotlin/com/google/protobuf/ExtensionList.kt \ + java/kotlin/src/main/kotlin/com/google/protobuf/OnlyForUseByGeneratedProtoCode.kt\ + java/kotlin/src/main/kotlin/com/google/protobuf/ProtoDslMarker.kt \ + java/kotlin/src/main/kotlin/com/google/protobuf/UnmodifiableCollections.kt \ + java/kotlin/src/test/kotlin/com/google/protobuf/DslListTest.kt \ + java/kotlin/src/test/kotlin/com/google/protobuf/DslMapTest.kt \ + java/kotlin/src/test/kotlin/com/google/protobuf/ExtendableMessageExtensionsTest.kt\ + java/kotlin/src/test/kotlin/com/google/protobuf/ExtensionListTest.kt \ + java/kotlin/src/test/kotlin/com/google/protobuf/Proto2Test.kt \ + java/kotlin/src/test/kotlin/com/google/protobuf/Proto3Test.kt \ + java/kotlin/src/test/proto/com/google/protobuf/evil_names_proto2.proto \ + java/kotlin/src/test/proto/com/google/protobuf/evil_names_proto3.proto \ + java/kotlin/src/test/proto/com/google/protobuf/example_extensible_message.proto \ + java/kotlin/src/test/proto/com/google/protobuf/multiple_files_proto3.proto \ + java/kotlin-lite/generate-sources-build.xml \ + java/kotlin-lite/generate-test-sources-build.xml \ + java/kotlin-lite/lite.awk \ + java/kotlin-lite/pom.xml \ + java/kotlin-lite/process-lite-sources-build.xml \ + java/kotlin-lite/src/test/kotlin/com/google/protobuf/ExtendableMessageLiteExtensionsTest.kt\ + java/kotlin-lite/src/test/kotlin/com/google/protobuf/Proto2LiteTest.kt \ java/lite.md \ java/lite/BUILD \ java/lite/generate-sources-build.xml \ diff --git a/cmake/extract_includes.bat.in b/cmake/extract_includes.bat.in index 5c5799efa1e..ad630af5118 100644 --- a/cmake/extract_includes.bat.in +++ b/cmake/extract_includes.bat.in @@ -26,6 +26,7 @@ copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\csharp\cshar copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\csharp\csharp_names.h" include\google\protobuf\compiler\csharp\csharp_names.h copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\importer.h" include\google\protobuf\compiler\importer.h copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\java\java_generator.h" include\google\protobuf\compiler\java\java_generator.h +copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\java\java_kotlin_generator.h" include\google\protobuf\compiler\java\java_kotlin_generator.h copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\java\java_names.h" include\google\protobuf\compiler\java\java_names.h copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\js\js_generator.h" include\google\protobuf\compiler\js\js_generator.h copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\js\well_known_types_embed.h" include\google\protobuf\compiler\js\well_known_types_embed.h diff --git a/cmake/libprotoc.cmake b/cmake/libprotoc.cmake index ecb5a851b8a..6316d83b861 100644 --- a/cmake/libprotoc.cmake +++ b/cmake/libprotoc.cmake @@ -44,6 +44,7 @@ set(libprotoc_files ${protobuf_source_dir}/src/google/protobuf/compiler/java/java_generator.cc ${protobuf_source_dir}/src/google/protobuf/compiler/java/java_generator_factory.cc ${protobuf_source_dir}/src/google/protobuf/compiler/java/java_helpers.cc + ${protobuf_source_dir}/src/google/protobuf/compiler/java/java_kotlin_generator.cc ${protobuf_source_dir}/src/google/protobuf/compiler/java/java_map_field.cc ${protobuf_source_dir}/src/google/protobuf/compiler/java/java_map_field_lite.cc ${protobuf_source_dir}/src/google/protobuf/compiler/java/java_message.cc diff --git a/java/README.md b/java/README.md index c3434144b1e..205c481e455 100644 --- a/java/README.md +++ b/java/README.md @@ -178,3 +178,8 @@ The complete documentation for Protocol Buffers is available via the web at: https://developers.google.com/protocol-buffers/ + +## Kotlin Protocol Buffers + +Code to support more idiomatic Kotlin protocol buffers has been added to the +repository, and Kotlin support will be launched in the next numbered release. diff --git a/java/kotlin-lite/generate-sources-build.xml b/java/kotlin-lite/generate-sources-build.xml new file mode 100644 index 00000000000..ab9cfea4e3b --- /dev/null +++ b/java/kotlin-lite/generate-sources-build.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/java/kotlin-lite/generate-test-sources-build.xml b/java/kotlin-lite/generate-test-sources-build.xml new file mode 100644 index 00000000000..c5f60b86536 --- /dev/null +++ b/java/kotlin-lite/generate-test-sources-build.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/java/kotlin-lite/lite.awk b/java/kotlin-lite/lite.awk new file mode 100644 index 00000000000..b22d965b6fd --- /dev/null +++ b/java/kotlin-lite/lite.awk @@ -0,0 +1,25 @@ +# Remove code enclosed by "BEGIN FULL-RUNTIME" and "END FULL-RUNTIME" to +# create the lite-only version of a test file. + +BEGIN { + in_full_runtime = 0; +} + +/BEGIN FULL-RUNTIME/ { + in_full_runtime = 1; + next; +} + +/END FULL-RUNTIME/ { + in_full_runtime = 0; + next; +} + +in_full_runtime { + # Skip full runtime code path. + next; +} + +{ + print; +} diff --git a/java/kotlin-lite/pom.xml b/java/kotlin-lite/pom.xml new file mode 100644 index 00000000000..80b4c4ffaea --- /dev/null +++ b/java/kotlin-lite/pom.xml @@ -0,0 +1,284 @@ + + + 4.0.0 + + com.google.protobuf + protobuf-parent + 3.15.6 + + + protobuf-kotlin-lite + bundle + + Protocol Buffers [Lite] + + Lite version of Protocol Buffers library. This version is optimized for code size, but does + not guarantee API/ABI stability. + + + + 1.4.31 + + + + + ${project.groupId} + protobuf-javalite + ${project.version} + + + junit + junit + test + + + org.easymock + easymock + test + + + org.easymock + easymockclassextension + test + + + com.google.guava + guava + test + + + com.google.guava + guava-testlib + test + + + com.google.truth + truth + test + + + org.jetbrains.kotlin + kotlin-stdlib + ${kotlin.version} + + + org.jetbrains.kotlin + kotlin-test + ${kotlin.version} + + + + + + + + ${protobuf.source.dir} + + google/protobuf/any.proto + google/protobuf/api.proto + google/protobuf/duration.proto + google/protobuf/empty.proto + google/protobuf/field_mask.proto + google/protobuf/source_context.proto + google/protobuf/struct.proto + google/protobuf/timestamp.proto + google/protobuf/type.proto + google/protobuf/wrappers.proto + + + + + + ${protobuf.source.dir} + + google/protobuf/testdata/golden_message_oneof_implemented + google/protobuf/testdata/golden_packed_fields_message + + + + + + + maven-resources-plugin + 3.1.0 + + + copy-kotlin-source-files + generate-sources + + copy-resources + + + ${generated.sources.dir}/com/google/protobuf + + + ${basedir}/../kotlin/src/main/kotlin/com/google/protobuf + + DslList.kt + DslMap.kt + DslProxy.kt + ExtendableMessageLiteExtensions.kt + ExtensionList.kt + OnlyForUseByGeneratedProtoCode.kt + ProtoDslMarker.kt + UnmodifiableCollections.kt + + + + + + + copy-test-source-files + generate-test-sources + + copy-resources + + + ${generated.testsources.dir}/com/google/protobuf + + + ${basedir}/../core/src/test/java/com/google/protobuf + + TestUtilLite.java + + + + ${basedir}/../kotlin/src/test/kotlin/com/google/protobuf + + ExtendableMessageExtensionsTest.kt + Proto2Test.kt + ProtoUtil.java + + + + + + + + + + + maven-antrun-plugin + + + + generate-sources + generate-sources + + + + + + + run + + + + + + generate-test-sources + generate-test-sources + + + + + + + run + + + + + process-lite-sources + generate-test-sources + + + + + + + run + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + add-generated-sources + generate-sources + + add-source + + + + ${generated.sources.dir} + + + + + + add-generated-test-sources + generate-test-sources + + add-test-source + + + + ${generated.testsources.dir} + + + + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + true + + + compile + compile + + + ${generated.sources.dir} + ${project.basedir}/src/main/kotlin + + + + + test-compile + test-compile + + + ${project.basedir}/src/test/kotlin + ${generated.testsources.dir} + + + + + + + + + org.apache.felix + maven-bundle-plugin + true + + + com.google.protobuf + https://developers.google.com/protocol-buffers/ + com.google.protobuf + com.google.protobuf;version=${project.version} + sun.misc;resolution:=optional,* + + + + + + diff --git a/java/kotlin-lite/process-lite-sources-build.xml b/java/kotlin-lite/process-lite-sources-build.xml new file mode 100644 index 00000000000..d49cf3aac03 --- /dev/null +++ b/java/kotlin-lite/process-lite-sources-build.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/java/kotlin-lite/src/test/kotlin/com/google/protobuf/ExtendableMessageLiteExtensionsTest.kt b/java/kotlin-lite/src/test/kotlin/com/google/protobuf/ExtendableMessageLiteExtensionsTest.kt new file mode 100644 index 00000000000..18933d1a345 --- /dev/null +++ b/java/kotlin-lite/src/test/kotlin/com/google/protobuf/ExtendableMessageLiteExtensionsTest.kt @@ -0,0 +1,60 @@ +package com.google.protobuf.kotlin + +import com.google.common.truth.Truth.assertThat +import example_extensible_message.ExampleExtensibleMessage +import example_extensible_message.ExampleExtensibleMessageOuterClass as TestProto +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@RunWith(JUnit4::class) +class ExtendableMessageLiteExtensionsTest { + @Test + fun setOnBuilder() { + val builder = ExampleExtensibleMessage.newBuilder() + builder[TestProto.int32Extension] = 5 + assertThat(builder.build().getExtension(TestProto.int32Extension)).isEqualTo(5) + } + + @Test + fun getOnBuilder() { + val builder = ExampleExtensibleMessage.newBuilder() + .setExtension(TestProto.int32Extension, 6) + assertThat(builder[TestProto.int32Extension]).isEqualTo(6) + } + + @Test + fun getOnMessage() { + val message = ExampleExtensibleMessage.newBuilder() + .setExtension(TestProto.int32Extension, 6) + .build() + assertThat(message[TestProto.int32Extension]).isEqualTo(6) + } + + @Test + fun containsPositiveOnMessage() { + val message = ExampleExtensibleMessage.newBuilder() + .setExtension(TestProto.int32Extension, 6) + .build() + assertThat(TestProto.int32Extension in message).isTrue() + } + + @Test + fun containsPositiveOnBuilder() { + val builder = ExampleExtensibleMessage.newBuilder() + .setExtension(TestProto.int32Extension, 6) + assertThat(TestProto.int32Extension in builder).isTrue() + } + + @Test + fun containsNegativeOnMessage() { + val message = ExampleExtensibleMessage.newBuilder().build() + assertThat(TestProto.int32Extension in message).isFalse() + } + + @Test + fun containsNegativeOnBuilder() { + val builder = ExampleExtensibleMessage.newBuilder() + assertThat(TestProto.int32Extension in builder).isFalse() + } +} diff --git a/java/kotlin-lite/src/test/kotlin/com/google/protobuf/Proto2LiteTest.kt b/java/kotlin-lite/src/test/kotlin/com/google/protobuf/Proto2LiteTest.kt new file mode 100644 index 00000000000..8726bbf9f90 --- /dev/null +++ b/java/kotlin-lite/src/test/kotlin/com/google/protobuf/Proto2LiteTest.kt @@ -0,0 +1,993 @@ +package com.google.protobuf.kotlin + +import com.google.common.truth.Truth.assertThat +import com.google.protobuf.TestAllTypesLiteKt +import com.google.protobuf.TestAllTypesLiteKt.nestedMessage +import com.google.protobuf.TestUtilLite +import com.google.protobuf.TestUtilLite.toBytes +import com.google.protobuf.UnittestImportLite.ImportEnumLite +import com.google.protobuf.UnittestImportLite.ImportMessageLite +import com.google.protobuf.UnittestImportPublicLite.PublicImportMessageLite +import com.google.protobuf.UnittestLite +import com.google.protobuf.UnittestLite.ForeignEnumLite +import com.google.protobuf.UnittestLite.TestAllTypesLite +import com.google.protobuf.UnittestLite.TestAllTypesLite.NestedEnum +import com.google.protobuf.UnittestLite.TestEmptyMessageLite +import com.google.protobuf.UnittestLite.TestEmptyMessageWithExtensionsLite +import com.google.protobuf.copy +import com.google.protobuf.foreignMessageLite +import evil_names_proto2.EvilNamesProto2OuterClass.EvilNamesProto2 +import evil_names_proto2.EvilNamesProto2OuterClass.HardKeywordsAllTypes +import evil_names_proto2.EvilNamesProto2OuterClass.Interface +import evil_names_proto2.HardKeywordsAllTypesKt +import evil_names_proto2.evilNamesProto2 +import evil_names_proto2.hardKeywordsAllTypes +import evil_names_proto2.interface_ +import com.google.protobuf.optionalGroupExtensionLite +import com.google.protobuf.repeatedGroupExtensionLite +import com.google.protobuf.testAllExtensionsLite +import com.google.protobuf.testAllTypesLite +import com.google.protobuf.testEmptyMessageLite +import com.google.protobuf.testEmptyMessageWithExtensionsLite +import protobuf_unittest.MapLiteUnittest.MapEnumLite +import protobuf_unittest.MapLiteUnittest.TestMapLite +import protobuf_unittest.testMapLite +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@RunWith(JUnit4::class) +class Proto2LiteTest { + @Test + fun testSetters() { + assertThat( + testAllTypesLite { + optionalInt32 = 101 + optionalInt64 = 102 + optionalUint32 = 103 + optionalUint64 = 104 + optionalSint32 = 105 + optionalSint64 = 106 + optionalFixed32 = 107 + optionalFixed64 = 108 + optionalSfixed32 = 109 + optionalSfixed64 = 110 + optionalFloat = 111.0f + optionalDouble = 112.0 + optionalBool = true + optionalString = "115" + optionalBytes = toBytes("116") + optionalGroup = + TestAllTypesLiteKt.optionalGroup { a = 117 } + optionalNestedMessage = nestedMessage { bb = 118 } + optionalForeignMessage = + foreignMessageLite { c = 119 } + optionalImportMessage = + ImportMessageLite.newBuilder().setD(120).build() + optionalPublicImportMessage = + PublicImportMessageLite.newBuilder().setE(126).build() + optionalLazyMessage = nestedMessage { bb = 127 } + optionalNestedEnum = NestedEnum.BAZ + optionalForeignEnum = ForeignEnumLite.FOREIGN_LITE_BAZ + optionalImportEnum = ImportEnumLite.IMPORT_LITE_BAZ + optionalStringPiece = "124" + optionalCord = "125" + repeatedInt32.add(201) + repeatedInt64.add(202) + repeatedUint32.add(203) + repeatedUint64.add(204) + repeatedSint32.add(205) + repeatedSint64.add(206) + repeatedFixed32.add(207) + repeatedFixed64.add(208) + repeatedSfixed32.add(209) + repeatedSfixed64.add(210) + repeatedFloat.add(211f) + repeatedDouble.add(212.0) + repeatedBool.add(true) + repeatedString.add("215") + repeatedBytes.add(toBytes("216")) + repeatedGroup.add(TestAllTypesLiteKt.repeatedGroup { a = 217 }) + repeatedNestedMessage.add(nestedMessage { bb = 218 }) + repeatedForeignMessage.add( + foreignMessageLite { c = 219 } + ) + repeatedImportMessage.add( + ImportMessageLite.newBuilder().setD(220).build() + ) + repeatedLazyMessage.add(nestedMessage { bb = 227 }) + repeatedNestedEnum.add(NestedEnum.BAR) + repeatedForeignEnum.add(ForeignEnumLite.FOREIGN_LITE_BAR) + repeatedImportEnum.add(ImportEnumLite.IMPORT_LITE_BAR) + repeatedStringPiece.add("224") + repeatedCord.add("225") + repeatedInt32 += 301 + repeatedInt64 += 302 + repeatedUint32 += 303 + repeatedUint64 += 304 + repeatedSint32 += 305 + repeatedSint64 += 306 + repeatedFixed32 += 307 + repeatedFixed64 += 308 + repeatedSfixed32 += 309 + repeatedSfixed64 += 310 + repeatedFloat += 311f + repeatedDouble += 312.0 + repeatedBool += false + repeatedString += "315" + repeatedBytes += toBytes("316") + repeatedGroup += TestAllTypesLiteKt.repeatedGroup { a = 317 } + repeatedNestedMessage += nestedMessage { bb = 318 } + repeatedForeignMessage += + foreignMessageLite { c = 319 } + repeatedImportMessage += + ImportMessageLite.newBuilder().setD(320).build() + repeatedLazyMessage += + TestAllTypesLiteKt.nestedMessage { bb = 327 } + repeatedNestedEnum += NestedEnum.BAZ + repeatedForeignEnum += ForeignEnumLite.FOREIGN_LITE_BAZ + repeatedImportEnum += ImportEnumLite.IMPORT_LITE_BAZ + repeatedStringPiece += "324" + repeatedCord += "325" + defaultInt32 = 401 + defaultInt64 = 402 + defaultUint32 = 403 + defaultUint64 = 404 + defaultSint32 = 405 + defaultSint64 = 406 + defaultFixed32 = 407 + defaultFixed64 = 408 + defaultSfixed32 = 409 + defaultSfixed64 = 410 + defaultFloat = 411f + defaultDouble = 412.0 + defaultBool = false + defaultString = "415" + defaultBytes = toBytes("416") + defaultNestedEnum = NestedEnum.FOO + defaultForeignEnum = ForeignEnumLite.FOREIGN_LITE_FOO + defaultImportEnum = ImportEnumLite.IMPORT_LITE_FOO + defaultStringPiece = "424" + defaultCord = "425" + oneofUint32 = 601 + oneofNestedMessage = + TestAllTypesLiteKt.nestedMessage { bb = 602 } + oneofString = "603" + oneofBytes = toBytes("604") + } + ).isEqualTo( + TestUtilLite.getAllLiteSetBuilder().build() + ) + } + + @Test + fun testGetters() { + testAllTypesLite { + optionalInt32 = 101 + assertThat(optionalInt32).isEqualTo(101) + optionalString = "115" + assertThat(optionalString).isEqualTo("115") + optionalGroup = TestAllTypesLiteKt.optionalGroup { a = 117 } + assertThat(optionalGroup).isEqualTo(TestAllTypesLiteKt.optionalGroup { a = 117 }) + optionalNestedMessage = TestAllTypesLiteKt.nestedMessage { bb = 118 } + assertThat(optionalNestedMessage).isEqualTo(TestAllTypesLiteKt.nestedMessage { bb = 118 }) + optionalNestedEnum = NestedEnum.BAZ + assertThat(optionalNestedEnum).isEqualTo(NestedEnum.BAZ) + defaultInt32 = 401 + assertThat(defaultInt32).isEqualTo(401) + oneofUint32 = 601 + assertThat(oneofUint32).isEqualTo(601) + } + } + + @Test + fun testDefaultGetters() { + testAllTypesLite { + assertThat(defaultInt32).isEqualTo(41) + assertThat(defaultString).isEqualTo("hello") + assertThat(defaultNestedEnum).isEqualTo(NestedEnum.BAR) + assertThat(defaultStringPiece).isEqualTo("abc") + } + } + + @Test + fun testRepeatedGettersAndSetters() { + testAllTypesLite { + repeatedInt32.addAll(listOf(1, 2)) + assertThat(repeatedInt32).isEqualTo(listOf(1, 2)) + repeatedInt32 += listOf(3, 4) + assertThat(repeatedInt32).isEqualTo(listOf(1, 2, 3, 4)) + repeatedInt32[0] = 5 + assertThat(repeatedInt32).isEqualTo(listOf(5, 2, 3, 4)) + + repeatedString.addAll(listOf("1", "2")) + assertThat(repeatedString).isEqualTo(listOf("1", "2")) + repeatedString += listOf("3", "4") + assertThat(repeatedString).isEqualTo(listOf("1", "2", "3", "4")) + repeatedString[0] = "5" + assertThat(repeatedString).isEqualTo(listOf("5", "2", "3", "4")) + + repeatedGroup.addAll( + listOf( + TestAllTypesLiteKt.repeatedGroup { a = 1 }, + TestAllTypesLiteKt.repeatedGroup { a = 2 } + ) + ) + assertThat(repeatedGroup).isEqualTo( + listOf( + TestAllTypesLiteKt.repeatedGroup { a = 1 }, + TestAllTypesLiteKt.repeatedGroup { a = 2 } + ) + ) + repeatedGroup += + listOf( + TestAllTypesLiteKt.repeatedGroup { a = 3 }, + TestAllTypesLiteKt.repeatedGroup { a = 4 } + ) + assertThat(repeatedGroup).isEqualTo( + listOf( + TestAllTypesLiteKt.repeatedGroup { a = 1 }, + TestAllTypesLiteKt.repeatedGroup { a = 2 }, + TestAllTypesLiteKt.repeatedGroup { a = 3 }, + TestAllTypesLiteKt.repeatedGroup { a = 4 } + ) + ) + repeatedGroup[0] = TestAllTypesLiteKt.repeatedGroup { a = 5 } + assertThat(repeatedGroup).isEqualTo( + listOf( + TestAllTypesLiteKt.repeatedGroup { a = 5 }, + TestAllTypesLiteKt.repeatedGroup { a = 2 }, + TestAllTypesLiteKt.repeatedGroup { a = 3 }, + TestAllTypesLiteKt.repeatedGroup { a = 4 } + ) + ) + + repeatedNestedMessage.addAll(listOf(nestedMessage { bb = 1 }, nestedMessage { bb = 2 })) + assertThat(repeatedNestedMessage).isEqualTo( + listOf( + nestedMessage { bb = 1 }, + nestedMessage { bb = 2 } + ) + ) + repeatedNestedMessage += listOf(nestedMessage { bb = 3 }, nestedMessage { bb = 4 }) + assertThat(repeatedNestedMessage).isEqualTo( + listOf( + nestedMessage { bb = 1 }, + nestedMessage { bb = 2 }, + nestedMessage { bb = 3 }, + nestedMessage { bb = 4 } + ) + ) + repeatedNestedMessage[0] = nestedMessage { bb = 5 } + assertThat(repeatedNestedMessage).isEqualTo( + listOf( + nestedMessage { bb = 5 }, + nestedMessage { bb = 2 }, + nestedMessage { bb = 3 }, + nestedMessage { bb = 4 } + ) + ) + + repeatedNestedEnum.addAll(listOf(NestedEnum.FOO, NestedEnum.BAR)) + assertThat(repeatedNestedEnum).isEqualTo(listOf(NestedEnum.FOO, NestedEnum.BAR)) + repeatedNestedEnum += listOf(NestedEnum.BAZ, NestedEnum.FOO) + assertThat(repeatedNestedEnum).isEqualTo( + listOf(NestedEnum.FOO, NestedEnum.BAR, NestedEnum.BAZ, NestedEnum.FOO) + ) + repeatedNestedEnum[0] = NestedEnum.BAR + assertThat(repeatedNestedEnum).isEqualTo( + listOf(NestedEnum.BAR, NestedEnum.BAR, NestedEnum.BAZ, NestedEnum.FOO) + ) + } + } + + @Test + fun testHazzers() { + testAllTypesLite { + optionalInt32 = 101 + assertThat(hasOptionalInt32()).isTrue() + assertThat(hasOptionalString()).isFalse() + optionalGroup = TestAllTypesLiteKt.optionalGroup { a = 117 } + assertThat(hasOptionalGroup()).isTrue() + assertThat(hasOptionalNestedMessage()).isFalse() + optionalNestedEnum = NestedEnum.BAZ + assertThat(hasOptionalNestedEnum()).isTrue() + assertThat(hasDefaultInt32()).isFalse() + oneofUint32 = 601 + assertThat(hasOneofUint32()).isTrue() + } + + testAllTypesLite { + assertThat(hasOptionalInt32()).isFalse() + optionalString = "115" + assertThat(hasOptionalString()).isTrue() + assertThat(hasOptionalGroup()).isFalse() + optionalNestedMessage = TestAllTypesLiteKt.nestedMessage { bb = 118 } + assertThat(hasOptionalNestedMessage()).isTrue() + assertThat(hasOptionalNestedEnum()).isFalse() + defaultInt32 = 401 + assertThat(hasDefaultInt32()).isTrue() + assertThat(hasOneofUint32()).isFalse() + } + } + + @Test + fun testClears() { + testAllTypesLite { + optionalInt32 = 101 + clearOptionalInt32() + assertThat(hasOptionalInt32()).isFalse() + + optionalString = "115" + clearOptionalString() + assertThat(hasOptionalString()).isFalse() + + optionalGroup = TestAllTypesLiteKt.optionalGroup { a = 117 } + clearOptionalGroup() + assertThat(hasOptionalGroup()).isFalse() + + optionalNestedMessage = TestAllTypesLiteKt.nestedMessage { bb = 118 } + clearOptionalNestedMessage() + assertThat(hasOptionalNestedMessage()).isFalse() + + optionalNestedEnum = NestedEnum.BAZ + clearOptionalNestedEnum() + assertThat(hasOptionalNestedEnum()).isFalse() + + defaultInt32 = 401 + clearDefaultInt32() + assertThat(hasDefaultInt32()).isFalse() + + oneofUint32 = 601 + clearOneofUint32() + assertThat(hasOneofUint32()).isFalse() + } + } + + @Test + fun testCopy() { + val message = testAllTypesLite { + optionalInt32 = 101 + optionalString = "115" + } + val modifiedMessage = message.copy { + optionalInt32 = 201 + } + + assertThat(message).isEqualTo( + TestAllTypesLite.newBuilder() + .setOptionalInt32(101) + .setOptionalString("115") + .build() + ) + assertThat(modifiedMessage).isEqualTo( + TestAllTypesLite.newBuilder() + .setOptionalInt32(201) + .setOptionalString("115") + .build() + ) + } + + @Test + fun testOneof() { + val message = testAllTypesLite { + oneofString = "foo" + assertThat(oneofFieldCase) + .isEqualTo(TestAllTypesLite.OneofFieldCase.ONEOF_STRING) + assertThat(oneofString).isEqualTo("foo") + clearOneofField() + assertThat(hasOneofUint32()).isFalse() + assertThat(oneofFieldCase) + .isEqualTo(TestAllTypesLite.OneofFieldCase.ONEOFFIELD_NOT_SET) + oneofUint32 = 5 + } + + assertThat(message.getOneofFieldCase()) + .isEqualTo(TestAllTypesLite.OneofFieldCase.ONEOF_UINT32) + assertThat(message.getOneofUint32()).isEqualTo(5) + } + + @Test + fun testExtensionsSet() { + assertThat( + testAllExtensionsLite { + this[UnittestLite.optionalInt32ExtensionLite] = 101 + this[UnittestLite.optionalInt64ExtensionLite] = 102L + this[UnittestLite.optionalUint32ExtensionLite] = 103 + this[UnittestLite.optionalUint64ExtensionLite] = 104L + this[UnittestLite.optionalSint32ExtensionLite] = 105 + this[UnittestLite.optionalSint64ExtensionLite] = 106L + this[UnittestLite.optionalFixed32ExtensionLite] = 107 + this[UnittestLite.optionalFixed64ExtensionLite] = 108L + this[UnittestLite.optionalSfixed32ExtensionLite] = 109 + this[UnittestLite.optionalSfixed64ExtensionLite] = 110L + this[UnittestLite.optionalFloatExtensionLite] = 111F + this[UnittestLite.optionalDoubleExtensionLite] = 112.0 + this[UnittestLite.optionalBoolExtensionLite] = true + this[UnittestLite.optionalStringExtensionLite] = "115" + this[UnittestLite.optionalBytesExtensionLite] = toBytes("116") + this[UnittestLite.optionalGroupExtensionLite] = optionalGroupExtensionLite { a = 117 } + this[UnittestLite.optionalNestedMessageExtensionLite] = + TestAllTypesLiteKt.nestedMessage { bb = 118 } + this[UnittestLite.optionalForeignMessageExtensionLite] = foreignMessageLite { c = 119 } + this[UnittestLite.optionalImportMessageExtensionLite] = + ImportMessageLite.newBuilder().setD(120).build() + this[UnittestLite.optionalPublicImportMessageExtensionLite] = + PublicImportMessageLite.newBuilder().setE(126).build() + this[UnittestLite.optionalLazyMessageExtensionLite] = + TestAllTypesLiteKt.nestedMessage { bb = 127 } + this[UnittestLite.optionalNestedEnumExtensionLite] = NestedEnum.BAZ + this[UnittestLite.optionalForeignEnumExtensionLite] = ForeignEnumLite.FOREIGN_LITE_BAZ + this[UnittestLite.optionalImportEnumExtensionLite] = ImportEnumLite.IMPORT_LITE_BAZ + this[UnittestLite.optionalStringPieceExtensionLite] = "124" + this[UnittestLite.optionalCordExtensionLite] = "125" + this[UnittestLite.repeatedInt32ExtensionLite].add(201) + this[UnittestLite.repeatedInt64ExtensionLite].add(202L) + this[UnittestLite.repeatedUint32ExtensionLite].add(203) + this[UnittestLite.repeatedUint64ExtensionLite].add(204L) + this[UnittestLite.repeatedSint32ExtensionLite].add(205) + this[UnittestLite.repeatedSint64ExtensionLite].add(206L) + this[UnittestLite.repeatedFixed32ExtensionLite].add(207) + this[UnittestLite.repeatedFixed64ExtensionLite].add(208L) + this[UnittestLite.repeatedSfixed32ExtensionLite].add(209) + this[UnittestLite.repeatedSfixed64ExtensionLite].add(210L) + this[UnittestLite.repeatedFloatExtensionLite].add(211F) + this[UnittestLite.repeatedDoubleExtensionLite].add(212.0) + this[UnittestLite.repeatedBoolExtensionLite].add(true) + this[UnittestLite.repeatedStringExtensionLite].add("215") + this[UnittestLite.repeatedBytesExtensionLite].add(toBytes("216")) + this[UnittestLite.repeatedGroupExtensionLite].add(repeatedGroupExtensionLite { a = 217 }) + this[UnittestLite.repeatedNestedMessageExtensionLite].add( + TestAllTypesLiteKt.nestedMessage { bb = 218 } + ) + this[UnittestLite.repeatedForeignMessageExtensionLite].add(foreignMessageLite { c = 219 }) + this[UnittestLite.repeatedImportMessageExtensionLite].add( + ImportMessageLite.newBuilder().setD(220).build() + ) + this[UnittestLite.repeatedLazyMessageExtensionLite].add( + TestAllTypesLiteKt.nestedMessage { bb = 227 } + ) + this[UnittestLite.repeatedNestedEnumExtensionLite].add(NestedEnum.BAR) + this[UnittestLite.repeatedForeignEnumExtensionLite].add(ForeignEnumLite.FOREIGN_LITE_BAR) + this[UnittestLite.repeatedImportEnumExtensionLite].add(ImportEnumLite.IMPORT_LITE_BAR) + this[UnittestLite.repeatedStringPieceExtensionLite].add("224") + this[UnittestLite.repeatedCordExtensionLite].add("225") + this[UnittestLite.repeatedInt32ExtensionLite].add(301) + this[UnittestLite.repeatedInt64ExtensionLite].add(302L) + this[UnittestLite.repeatedUint32ExtensionLite].add(303) + this[UnittestLite.repeatedUint64ExtensionLite].add(304L) + this[UnittestLite.repeatedSint32ExtensionLite].add(305) + this[UnittestLite.repeatedSint64ExtensionLite].add(306L) + this[UnittestLite.repeatedFixed32ExtensionLite].add(307) + this[UnittestLite.repeatedFixed64ExtensionLite].add(308L) + this[UnittestLite.repeatedSfixed32ExtensionLite].add(309) + this[UnittestLite.repeatedSfixed64ExtensionLite].add(310L) + this[UnittestLite.repeatedFloatExtensionLite].add(311F) + this[UnittestLite.repeatedDoubleExtensionLite].add(312.0) + this[UnittestLite.repeatedBoolExtensionLite].add(false) + this[UnittestLite.repeatedStringExtensionLite].add("315") + this[UnittestLite.repeatedBytesExtensionLite].add(toBytes("316")) + this[UnittestLite.repeatedGroupExtensionLite].add(repeatedGroupExtensionLite { a = 317 }) + this[UnittestLite.repeatedNestedMessageExtensionLite].add( + TestAllTypesLiteKt.nestedMessage { bb = 318 } + ) + this[UnittestLite.repeatedForeignMessageExtensionLite].add(foreignMessageLite { c = 319 }) + this[UnittestLite.repeatedImportMessageExtensionLite].add( + ImportMessageLite.newBuilder().setD(320).build() + ) + this[UnittestLite.repeatedLazyMessageExtensionLite].add( + TestAllTypesLiteKt.nestedMessage { bb = 327 } + ) + this[UnittestLite.repeatedNestedEnumExtensionLite].add(NestedEnum.BAZ) + this[UnittestLite.repeatedForeignEnumExtensionLite].add(ForeignEnumLite.FOREIGN_LITE_BAZ) + this[UnittestLite.repeatedImportEnumExtensionLite].add(ImportEnumLite.IMPORT_LITE_BAZ) + this[UnittestLite.repeatedStringPieceExtensionLite].add("324") + this[UnittestLite.repeatedCordExtensionLite].add("325") + this[UnittestLite.defaultInt32ExtensionLite] = 401 + this[UnittestLite.defaultInt64ExtensionLite] = 402L + this[UnittestLite.defaultUint32ExtensionLite] = 403 + this[UnittestLite.defaultUint64ExtensionLite] = 404L + this[UnittestLite.defaultSint32ExtensionLite] = 405 + this[UnittestLite.defaultSint64ExtensionLite] = 406L + this[UnittestLite.defaultFixed32ExtensionLite] = 407 + this[UnittestLite.defaultFixed64ExtensionLite] = 408L + this[UnittestLite.defaultSfixed32ExtensionLite] = 409 + this[UnittestLite.defaultSfixed64ExtensionLite] = 410L + this[UnittestLite.defaultFloatExtensionLite] = 411F + this[UnittestLite.defaultDoubleExtensionLite] = 412.0 + this[UnittestLite.defaultBoolExtensionLite] = false + this[UnittestLite.defaultStringExtensionLite] = "415" + this[UnittestLite.defaultBytesExtensionLite] = toBytes("416") + this[UnittestLite.defaultNestedEnumExtensionLite] = NestedEnum.FOO + this[UnittestLite.defaultForeignEnumExtensionLite] = ForeignEnumLite.FOREIGN_LITE_FOO + this[UnittestLite.defaultImportEnumExtensionLite] = ImportEnumLite.IMPORT_LITE_FOO + this[UnittestLite.defaultStringPieceExtensionLite] = "424" + this[UnittestLite.defaultCordExtensionLite] = "425" + this[UnittestLite.oneofUint32ExtensionLite] = 601 + this[UnittestLite.oneofNestedMessageExtensionLite] = + TestAllTypesLiteKt.nestedMessage { bb = 602 } + this[UnittestLite.oneofStringExtensionLite] = "603" + this[UnittestLite.oneofBytesExtensionLite] = toBytes("604") + } + ).isEqualTo( + TestUtilLite.getAllLiteExtensionsSet() + ) + } + + @Test + fun testExtensionGetters() { + testAllExtensionsLite { + this[UnittestLite.optionalInt32ExtensionLite] = 101 + assertThat(this[UnittestLite.optionalInt32ExtensionLite]).isEqualTo(101) + this[UnittestLite.optionalStringExtensionLite] = "115" + assertThat(this[UnittestLite.optionalStringExtensionLite]).isEqualTo("115") + this[UnittestLite.optionalGroupExtensionLite] = optionalGroupExtensionLite { a = 117 } + assertThat(this[UnittestLite.optionalGroupExtensionLite]) + .isEqualTo(optionalGroupExtensionLite { a = 117 }) + this[UnittestLite.optionalNestedMessageExtensionLite] = + TestAllTypesLiteKt.nestedMessage { bb = 118 } + assertThat(this[UnittestLite.optionalNestedMessageExtensionLite]) + .isEqualTo(TestAllTypesLiteKt.nestedMessage { bb = 118 }) + this[UnittestLite.optionalNestedEnumExtensionLite] = NestedEnum.BAZ + assertThat(this[UnittestLite.optionalNestedEnumExtensionLite]).isEqualTo(NestedEnum.BAZ) + this[UnittestLite.defaultInt32ExtensionLite] = 401 + assertThat(this[UnittestLite.defaultInt32ExtensionLite]).isEqualTo(401) + this[UnittestLite.oneofUint32ExtensionLite] = 601 + assertThat(this[UnittestLite.oneofUint32ExtensionLite]).isEqualTo(601) + } + } + + @Test + fun testRepeatedExtensionGettersAndSetters() { + testAllExtensionsLite { + this[UnittestLite.repeatedInt32ExtensionLite].addAll(listOf(1, 2)) + assertThat(this[UnittestLite.repeatedInt32ExtensionLite]).isEqualTo(listOf(1, 2)) + this[UnittestLite.repeatedInt32ExtensionLite].addAll(listOf(3, 4)) + assertThat(this[UnittestLite.repeatedInt32ExtensionLite]).isEqualTo(listOf(1, 2, 3, 4)) + this[UnittestLite.repeatedInt32ExtensionLite][0] = 5 + assertThat(this[UnittestLite.repeatedInt32ExtensionLite]).isEqualTo(listOf(5, 2, 3, 4)) + + this[UnittestLite.repeatedStringExtensionLite].addAll(listOf("1", "2")) + assertThat(this[UnittestLite.repeatedStringExtensionLite]).isEqualTo(listOf("1", "2")) + this[UnittestLite.repeatedStringExtensionLite].addAll(listOf("3", "4")) + assertThat(this[UnittestLite.repeatedStringExtensionLite]) + .isEqualTo(listOf("1", "2", "3", "4")) + this[UnittestLite.repeatedStringExtensionLite][0] = "5" + assertThat(this[UnittestLite.repeatedStringExtensionLite]) + .isEqualTo(listOf("5", "2", "3", "4")) + + this[UnittestLite.repeatedGroupExtensionLite].addAll( + listOf( + repeatedGroupExtensionLite { a = 1 }, + repeatedGroupExtensionLite { a = 2 } + ) + ) + assertThat(this[UnittestLite.repeatedGroupExtensionLite]).isEqualTo( + listOf( + repeatedGroupExtensionLite { a = 1 }, + repeatedGroupExtensionLite { a = 2 } + ) + ) + this[UnittestLite.repeatedGroupExtensionLite].addAll( + listOf( + repeatedGroupExtensionLite { a = 3 }, + repeatedGroupExtensionLite { a = 4 } + ) + ) + assertThat(this[UnittestLite.repeatedGroupExtensionLite]).isEqualTo( + listOf( + repeatedGroupExtensionLite { a = 1 }, + repeatedGroupExtensionLite { a = 2 }, + repeatedGroupExtensionLite { a = 3 }, + repeatedGroupExtensionLite { a = 4 } + ) + ) + this[UnittestLite.repeatedGroupExtensionLite][0] = repeatedGroupExtensionLite { a = 5 } + assertThat(this[UnittestLite.repeatedGroupExtensionLite]).isEqualTo( + listOf( + repeatedGroupExtensionLite { a = 5 }, + repeatedGroupExtensionLite { a = 2 }, + repeatedGroupExtensionLite { a = 3 }, + repeatedGroupExtensionLite { a = 4 } + ) + ) + + this[UnittestLite.repeatedNestedMessageExtensionLite].addAll( + listOf(nestedMessage { bb = 1 }, nestedMessage { bb = 2 }) + ) + assertThat(this[UnittestLite.repeatedNestedMessageExtensionLite]).isEqualTo( + listOf(nestedMessage { bb = 1 }, nestedMessage { bb = 2 }) + ) + this[UnittestLite.repeatedNestedMessageExtensionLite].addAll( + listOf(nestedMessage { bb = 3 }, nestedMessage { bb = 4 }) + ) + assertThat(this[UnittestLite.repeatedNestedMessageExtensionLite]).isEqualTo( + listOf( + nestedMessage { bb = 1 }, + nestedMessage { bb = 2 }, + nestedMessage { bb = 3 }, + nestedMessage { bb = 4 } + ) + ) + this[UnittestLite.repeatedNestedMessageExtensionLite][0] = nestedMessage { bb = 5 } + assertThat(this[UnittestLite.repeatedNestedMessageExtensionLite]).isEqualTo( + listOf( + nestedMessage { bb = 5 }, + nestedMessage { bb = 2 }, + nestedMessage { bb = 3 }, + nestedMessage { bb = 4 } + ) + ) + + this[UnittestLite.repeatedNestedEnumExtensionLite] + .addAll(listOf(NestedEnum.FOO, NestedEnum.BAR)) + assertThat(this[UnittestLite.repeatedNestedEnumExtensionLite]) + .isEqualTo(listOf(NestedEnum.FOO, NestedEnum.BAR)) + this[UnittestLite.repeatedNestedEnumExtensionLite].addAll(listOf(NestedEnum.BAZ, NestedEnum.FOO)) + assertThat(this[UnittestLite.repeatedNestedEnumExtensionLite]).isEqualTo( + listOf(NestedEnum.FOO, NestedEnum.BAR, NestedEnum.BAZ, NestedEnum.FOO) + ) + this[UnittestLite.repeatedNestedEnumExtensionLite][0] = NestedEnum.BAR + assertThat(this[UnittestLite.repeatedNestedEnumExtensionLite]).isEqualTo( + listOf(NestedEnum.BAR, NestedEnum.BAR, NestedEnum.BAZ, NestedEnum.FOO) + ) + } + } + + @Test + fun testExtensionContains() { + testAllExtensionsLite { + this[UnittestLite.optionalInt32ExtensionLite] = 101 + assertThat(contains(UnittestLite.optionalInt32ExtensionLite)).isTrue() + assertThat(contains(UnittestLite.optionalStringExtensionLite)).isFalse() + this[UnittestLite.optionalGroupExtensionLite] = optionalGroupExtensionLite { a = 117 } + assertThat(contains(UnittestLite.optionalGroupExtensionLite)).isTrue() + assertThat(contains(UnittestLite.optionalNestedMessageExtensionLite)).isFalse() + this[UnittestLite.optionalNestedEnumExtensionLite] = NestedEnum.BAZ + assertThat(contains(UnittestLite.optionalNestedEnumExtensionLite)).isTrue() + assertThat(contains(UnittestLite.defaultInt32ExtensionLite)).isFalse() + this[UnittestLite.oneofUint32ExtensionLite] = 601 + assertThat(contains(UnittestLite.oneofUint32ExtensionLite)).isTrue() + } + + testAllExtensionsLite { + assertThat(contains(UnittestLite.optionalInt32ExtensionLite)).isFalse() + this[UnittestLite.optionalStringExtensionLite] = "115" + assertThat(contains(UnittestLite.optionalStringExtensionLite)).isTrue() + assertThat(contains(UnittestLite.optionalGroupExtensionLite)).isFalse() + this[UnittestLite.optionalNestedMessageExtensionLite] = + TestAllTypesLiteKt.nestedMessage { bb = 118 } + assertThat(contains(UnittestLite.optionalNestedMessageExtensionLite)).isTrue() + assertThat(contains(UnittestLite.optionalNestedEnumExtensionLite)).isFalse() + this[UnittestLite.defaultInt32ExtensionLite] = 401 + assertThat(contains(UnittestLite.defaultInt32ExtensionLite)).isTrue() + assertThat(contains(UnittestLite.oneofUint32ExtensionLite)).isFalse() + } + } + + @Test + fun testExtensionClears() { + testAllExtensionsLite { + this[UnittestLite.optionalInt32ExtensionLite] = 101 + clear(UnittestLite.optionalInt32ExtensionLite) + assertThat(contains(UnittestLite.optionalInt32ExtensionLite)).isFalse() + + this[UnittestLite.optionalStringExtensionLite] = "115" + clear(UnittestLite.optionalStringExtensionLite) + assertThat(contains(UnittestLite.optionalStringExtensionLite)).isFalse() + + this[UnittestLite.optionalGroupExtensionLite] = optionalGroupExtensionLite { a = 117 } + clear(UnittestLite.optionalGroupExtensionLite) + assertThat(contains(UnittestLite.optionalGroupExtensionLite)).isFalse() + + this[UnittestLite.optionalNestedMessageExtensionLite] = + TestAllTypesLiteKt.nestedMessage { bb = 118 } + clear(UnittestLite.optionalNestedMessageExtensionLite) + assertThat(contains(UnittestLite.optionalNestedMessageExtensionLite)).isFalse() + + this[UnittestLite.optionalNestedEnumExtensionLite] = NestedEnum.BAZ + clear(UnittestLite.optionalNestedEnumExtensionLite) + assertThat(contains(UnittestLite.optionalNestedEnumExtensionLite)).isFalse() + + this[UnittestLite.defaultInt32ExtensionLite] = 401 + clear(UnittestLite.defaultInt32ExtensionLite) + assertThat(contains(UnittestLite.defaultInt32ExtensionLite)).isFalse() + + this[UnittestLite.oneofUint32ExtensionLite] = 601 + clear(UnittestLite.oneofUint32ExtensionLite) + assertThat(contains(UnittestLite.oneofUint32ExtensionLite)).isFalse() + } + } + + @Test + fun testEmptyMessages() { + assertThat( + testEmptyMessageLite {} + ).isEqualTo( + TestEmptyMessageLite.newBuilder().build() + ) + + assertThat( + testEmptyMessageWithExtensionsLite {} + ).isEqualTo( + TestEmptyMessageWithExtensionsLite.newBuilder().build() + ) + } + + @Test + fun testMapSetters() { + assertThat( + testMapLite { + mapInt32Int32[1] = 2 + mapInt64Int64[1L] = 2L + mapUint32Uint32[1] = 2 + mapUint64Uint64[1L] = 2L + mapSint32Sint32[1] = 2 + mapSint64Sint64[1L] = 2L + mapFixed32Fixed32[1] = 2 + mapFixed64Fixed64[1L] = 2L + mapSfixed32Sfixed32[1] = 2 + mapSfixed64Sfixed64[1L] = 2L + mapInt32Float[1] = 2F + mapInt32Double[1] = 2.0 + mapBoolBool[true] = true + mapStringString["1"] = "2" + mapInt32Bytes[1] = toBytes("2") + mapInt32Enum[1] = MapEnumLite.MAP_ENUM_FOO_LITE + mapInt32ForeignMessage[1] = foreignMessageLite { c = 1 } + } + ).isEqualTo( + TestMapLite.newBuilder() + .putMapInt32Int32(1, 2) + .putMapInt64Int64(1L, 2L) + .putMapUint32Uint32(1, 2) + .putMapUint64Uint64(1L, 2L) + .putMapSint32Sint32(1, 2) + .putMapSint64Sint64(1L, 2L) + .putMapFixed32Fixed32(1, 2) + .putMapFixed64Fixed64(1L, 2L) + .putMapSfixed32Sfixed32(1, 2) + .putMapSfixed64Sfixed64(1L, 2L) + .putMapInt32Float(1, 2F) + .putMapInt32Double(1, 2.0) + .putMapBoolBool(true, true) + .putMapStringString("1", "2") + .putMapInt32Bytes(1, toBytes("2")) + .putMapInt32Enum(1, MapEnumLite.MAP_ENUM_FOO_LITE) + .putMapInt32ForeignMessage(1, foreignMessageLite { c = 1 }) + .build() + ) + } + + @Test + fun testMapGettersAndSetters() { + testMapLite { + mapInt32Int32.put(1, 2) + assertThat(mapInt32Int32).isEqualTo(mapOf(1 to 2)) + mapInt32Int32[3] = 4 + assertThat(mapInt32Int32).isEqualTo(mapOf(1 to 2, 3 to 4)) + mapInt32Int32.putAll(mapOf(5 to 6, 7 to 8)) + assertThat(mapInt32Int32).isEqualTo(mapOf(1 to 2, 3 to 4, 5 to 6, 7 to 8)) + + mapStringString.put("1", "2") + assertThat(mapStringString).isEqualTo(mapOf("1" to "2")) + mapStringString["3"] = "4" + assertThat(mapStringString).isEqualTo(mapOf("1" to "2", "3" to "4")) + mapStringString.putAll(mapOf("5" to "6", "7" to "8")) + assertThat(mapStringString).isEqualTo(mapOf("1" to "2", "3" to "4", "5" to "6", "7" to "8")) + + mapInt32Enum.put(1, MapEnumLite.MAP_ENUM_FOO_LITE) + assertThat(mapInt32Enum).isEqualTo(mapOf(1 to MapEnumLite.MAP_ENUM_FOO_LITE)) + mapInt32Enum[2] = MapEnumLite.MAP_ENUM_BAR_LITE + assertThat(mapInt32Enum).isEqualTo( + mapOf(1 to MapEnumLite.MAP_ENUM_FOO_LITE, 2 to MapEnumLite.MAP_ENUM_BAR_LITE) + ) + mapInt32Enum.putAll( + mapOf(3 to MapEnumLite.MAP_ENUM_BAZ_LITE, 4 to MapEnumLite.MAP_ENUM_FOO_LITE) + ) + assertThat(mapInt32Enum).isEqualTo( + mapOf( + 1 to MapEnumLite.MAP_ENUM_FOO_LITE, + 2 to MapEnumLite.MAP_ENUM_BAR_LITE, + 3 to MapEnumLite.MAP_ENUM_BAZ_LITE, + 4 to MapEnumLite.MAP_ENUM_FOO_LITE + ) + ) + + mapInt32ForeignMessage.put(1, foreignMessageLite { c = 1 }) + assertThat(mapInt32ForeignMessage).isEqualTo(mapOf(1 to foreignMessageLite { c = 1 })) + mapInt32ForeignMessage[2] = foreignMessageLite { c = 2 } + assertThat(mapInt32ForeignMessage).isEqualTo( + mapOf(1 to foreignMessageLite { c = 1 }, 2 to foreignMessageLite { c = 2 }) + ) + mapInt32ForeignMessage.putAll( + mapOf(3 to foreignMessageLite { c = 3 }, 4 to foreignMessageLite { c = 4 }) + ) + assertThat(mapInt32ForeignMessage).isEqualTo( + mapOf( + 1 to foreignMessageLite { c = 1 }, + 2 to foreignMessageLite { c = 2 }, + 3 to foreignMessageLite { c = 3 }, + 4 to foreignMessageLite { c = 4 } + ) + ) + } + } + + @Test + fun testMapRemove() { + testMapLite { + mapInt32Int32.putAll(mapOf(1 to 2, 3 to 4)) + mapInt32Int32.remove(1) + assertThat(mapInt32Int32).isEqualTo(mapOf(3 to 4)) + + mapStringString.putAll(mapOf("1" to "2", "3" to "4")) + mapStringString.remove("1") + assertThat(mapStringString).isEqualTo(mapOf("3" to "4")) + + mapInt32Enum.putAll( + mapOf(1 to MapEnumLite.MAP_ENUM_FOO_LITE, 2 to MapEnumLite.MAP_ENUM_BAR_LITE) + ) + mapInt32Enum.remove(1) + assertThat(mapInt32Enum).isEqualTo(mapOf(2 to MapEnumLite.MAP_ENUM_BAR_LITE)) + + mapInt32ForeignMessage.putAll( + mapOf(1 to foreignMessageLite { c = 1 }, 2 to foreignMessageLite { c = 2 }) + ) + mapInt32ForeignMessage.remove(1) + assertThat(mapInt32ForeignMessage).isEqualTo(mapOf(2 to foreignMessageLite { c = 2 })) + } + } + + @Test + fun testMapClear() { + testMapLite { + mapInt32Int32.putAll(mapOf(1 to 2, 3 to 4)) + mapInt32Int32.clear() + assertThat(mapInt32Int32.isEmpty()).isTrue() + + mapStringString.putAll(mapOf("1" to "2", "3" to "4")) + mapStringString.clear() + assertThat(mapStringString.isEmpty()).isTrue() + + mapInt32Enum.putAll( + mapOf(1 to MapEnumLite.MAP_ENUM_FOO_LITE, 2 to MapEnumLite.MAP_ENUM_BAR_LITE) + ) + mapInt32Enum.clear() + assertThat(mapInt32Enum.isEmpty()).isTrue() + + mapInt32ForeignMessage.putAll( + mapOf(1 to foreignMessageLite { c = 1 }, 2 to foreignMessageLite { c = 2 }) + ) + mapInt32ForeignMessage.clear() + assertThat(mapInt32ForeignMessage.isEmpty()).isTrue() + } + } + + @Test + fun testEvilNames() { + assertThat( + evilNamesProto2 { + initialized = true + hasFoo = true + bar = "foo" + isInitialized = true + fooBar = "foo" + aLLCAPS += "foo" + aLLCAPSMAP[1] = true + hasUnderbarPrecedingNumeric1Foo = true + hasUnderbarPrecedingNumeric42Bar = true + hasUnderbarPrecedingNumeric123Foo42BarBaz = true + extension += "foo" + class_ += 1 + int = 1.0 + long = true + boolean = 1L + sealed = "foo" + interface_ = 1F + in_ = 1 + object_ = "foo" + cachedSize_ = "foo" + serializedSize_ = true + by = "foo" + } + ).isEqualTo( + EvilNamesProto2.newBuilder() + .setInitialized(true) + .setHasFoo(true) + .setBar("foo") + .setIsInitialized(true) + .setFooBar("foo") + .addALLCAPS("foo") + .putALLCAPSMAP(1, true) + .setHasUnderbarPrecedingNumeric1Foo(true) + .setHasUnderbarPrecedingNumeric42Bar(true) + .setHasUnderbarPrecedingNumeric123Foo42BarBaz(true) + .addExtension("foo") + .addClass_(1) + .setInt(1.0) + .setLong(true) + .setBoolean(1L) + .setSealed("foo") + .setInterface(1F) + .setIn(1) + .setObject("foo") + .setCachedSize_("foo") + .setSerializedSize_(true) + .setBy("foo") + .build() + ) + + assertThat(interface_ {}).isEqualTo(Interface.newBuilder().build()) + } + + @Test + fun testHardKeywordGettersAndSetters() { + hardKeywordsAllTypes { + as_ = 1 + assertThat(as_).isEqualTo(1) + + in_ = "foo" + assertThat(in_).isEqualTo("foo") + + break_ = HardKeywordsAllTypes.NestedEnum.FOO + assertThat(break_).isEqualTo(HardKeywordsAllTypes.NestedEnum.FOO) + + do_ = HardKeywordsAllTypesKt.nestedMessage { while_ = 1 } + assertThat(do_).isEqualTo(HardKeywordsAllTypesKt.nestedMessage { while_ = 1 }) + + continue_[1] = 1 + assertThat(continue_[1]).isEqualTo(1) + + else_ += 1 + assertThat(else_).isEqualTo(listOf(1)) + + for_ += "foo" + assertThat(for_).isEqualTo(listOf("foo")) + + fun_ += HardKeywordsAllTypes.NestedEnum.FOO + assertThat(fun_).isEqualTo(listOf(HardKeywordsAllTypes.NestedEnum.FOO)) + + if_ += HardKeywordsAllTypesKt.nestedMessage { while_ = 1 } + assertThat(if_).isEqualTo(listOf(HardKeywordsAllTypesKt.nestedMessage { while_ = 1 })) + } + } + + @Test + fun testHardKeywordHazzers() { + hardKeywordsAllTypes { + as_ = 1 + assertThat(hasAs_()).isTrue() + + in_ = "foo" + assertThat(hasIn_()).isTrue() + + break_ = HardKeywordsAllTypes.NestedEnum.FOO + assertThat(hasBreak_()).isTrue() + + do_ = HardKeywordsAllTypesKt.nestedMessage { while_ = 1 } + assertThat(hasDo_()).isTrue() + } + } + + @Test + fun testHardKeywordClears() { + hardKeywordsAllTypes { + as_ = 1 + clearAs_() + assertThat(hasAs_()).isFalse() + + in_ = "foo" + clearIn_() + assertThat(hasIn_()).isFalse() + + break_ = HardKeywordsAllTypes.NestedEnum.FOO + clearBreak_() + assertThat(hasBreak_()).isFalse() + + do_ = HardKeywordsAllTypesKt.nestedMessage { while_ = 1 } + clearDo_() + assertThat(hasDo_()).isFalse() + } + } +} diff --git a/java/kotlin/generate-sources-build.xml b/java/kotlin/generate-sources-build.xml new file mode 100644 index 00000000000..6963f3717f5 --- /dev/null +++ b/java/kotlin/generate-sources-build.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/java/kotlin/generate-test-sources-build.xml b/java/kotlin/generate-test-sources-build.xml new file mode 100644 index 00000000000..dca1a05350e --- /dev/null +++ b/java/kotlin/generate-test-sources-build.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/java/kotlin/pom.xml b/java/kotlin/pom.xml new file mode 100644 index 00000000000..5f281251753 --- /dev/null +++ b/java/kotlin/pom.xml @@ -0,0 +1,240 @@ + + + 4.0.0 + + com.google.protobuf + protobuf-parent + 3.15.6 + + + protobuf-kotlin + bundle + + Protocol Buffers [Core] + + Core Protocol Buffers library. Protocol Buffers are a way of encoding structured data in an + efficient yet extensible format. + + + + 1.4.31 + + + + + ${project.groupId} + protobuf-java + + + junit + junit + test + + + org.easymock + easymock + test + + + org.easymock + easymockclassextension + test + + + com.google.guava + guava + test + + + com.google.guava + guava-testlib + test + + + com.google.truth + truth + test + + + org.jetbrains.kotlin + kotlin-stdlib + ${kotlin.version} + + + org.jetbrains.kotlin + kotlin-test + ${kotlin.version} + test + + + + + + + + ${protobuf.source.dir} + + google/protobuf/any.proto + google/protobuf/api.proto + google/protobuf/descriptor.proto + google/protobuf/duration.proto + google/protobuf/empty.proto + google/protobuf/field_mask.proto + google/protobuf/source_context.proto + google/protobuf/struct.proto + google/protobuf/timestamp.proto + google/protobuf/type.proto + google/protobuf/wrappers.proto + google/protobuf/compiler/plugin.proto + + + + + + ${protobuf.source.dir} + + google/protobuf/testdata/golden_message_oneof_implemented + google/protobuf/testdata/golden_packed_fields_message + + + + + + + maven-resources-plugin + 3.1.0 + + + copy-test-source-files + generate-test-sources + + copy-resources + + + ${generated.testsources.dir}/com/google/protobuf + + + ${basedir}/../core/src/test/java/com/google/protobuf + + TestUtil.java + TestUtilLite.java + + + + + + + + + + + maven-antrun-plugin + + + + generate-sources + generate-sources + + + + + + + run + + + + + + generate-test-sources + generate-test-sources + + + + + + + run + + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + add-generated-sources + generate-sources + + add-source + + + + ${generated.sources.dir} + + + + + add-generated-test-sources + generate-test-sources + + add-test-source + + + + ${generated.testsources.dir} + + + + + + + org.jetbrains.kotlin + kotlin-maven-plugin + ${kotlin.version} + true + + + compile + compile + + + ${generated.sources.dir} + ${project.basedir}/src/main/kotlin + + + + + test-compile + test-compile + + + ${project.basedir}/src/test/kotlin + ${generated.testsources.dir} + + + + + + + + org.apache.felix + maven-bundle-plugin + true + + + com.google.protobuf + https://developers.google.com/protocol-buffers/ + com.google.protobuf + com.google.protobuf;version=${project.version} + sun.misc;resolution:=optional,* + + + + + + + diff --git a/java/kotlin/src/main/kotlin/com/google/protobuf/DslList.kt b/java/kotlin/src/main/kotlin/com/google/protobuf/DslList.kt new file mode 100644 index 00000000000..54a64af08eb --- /dev/null +++ b/java/kotlin/src/main/kotlin/com/google/protobuf/DslList.kt @@ -0,0 +1,27 @@ +package com.google.protobuf.kotlin + +/** + * A simple wrapper around a [List] with an extra generic parameter that can be used to disambiguate + * extension methods. + * + *

This class is used by Kotlin protocol buffer extensions, and its constructor is public only + * because generated message code is in a different compilation unit. Others should not use this + * class directly in any way. + */ +@Suppress("unused") // the unused type parameter +class DslList @OnlyForUseByGeneratedProtoCode constructor( + private val delegate: List +) : List by delegate { + override fun iterator(): Iterator = UnmodifiableIterator(delegate.iterator()) + + override fun listIterator(): ListIterator = UnmodifiableListIterator(delegate.listIterator()) + + override fun listIterator(index: Int): ListIterator = + UnmodifiableListIterator(delegate.listIterator(index)) + + override fun equals(other: Any?): Boolean = delegate == other + + override fun hashCode(): Int = delegate.hashCode() + + override fun toString(): String = delegate.toString() +} diff --git a/java/kotlin/src/main/kotlin/com/google/protobuf/DslMap.kt b/java/kotlin/src/main/kotlin/com/google/protobuf/DslMap.kt new file mode 100644 index 00000000000..9949fb4a8fe --- /dev/null +++ b/java/kotlin/src/main/kotlin/com/google/protobuf/DslMap.kt @@ -0,0 +1,30 @@ +package com.google.protobuf.kotlin + +/** + * A simple wrapper around a [Map] with an extra generic parameter that can be used to disambiguate + * extension methods. + * + *

This class is used by Kotlin protocol buffer extensions, and its constructor is public only + * because generated message code is in a different compilation unit. Others should not use this + * class directly in any way. + */ +@Suppress("unused") // the unused type parameter +class DslMap @OnlyForUseByGeneratedProtoCode constructor( + private val delegate: Map +) : Map by delegate { + // We allocate the wrappers on calls to get, not with lazy {...}, because lazy allocates + // a few objects up front, and any kind of query operation on this object should be rare. + + override val entries: Set> + get() = UnmodifiableMapEntries(delegate.entries) + override val keys: Set + get() = UnmodifiableSet(delegate.keys) + override val values: Collection + get() = UnmodifiableCollection(delegate.values) + + override fun equals(other: Any?): Boolean = delegate == other + + override fun hashCode(): Int = delegate.hashCode() + + override fun toString(): String = delegate.toString() +} diff --git a/java/kotlin/src/main/kotlin/com/google/protobuf/DslProxy.kt b/java/kotlin/src/main/kotlin/com/google/protobuf/DslProxy.kt new file mode 100644 index 00000000000..7d14e95c1e9 --- /dev/null +++ b/java/kotlin/src/main/kotlin/com/google/protobuf/DslProxy.kt @@ -0,0 +1,12 @@ +package com.google.protobuf.kotlin + +/** + * A type meaningful only for its existence, never intended to be instantiated. For example, + * a `DslList` can be given different extension methods than a + * `DslList`. + */ +abstract class DslProxy @OnlyForUseByGeneratedProtoCode protected constructor() { + init { + throw UnsupportedOperationException("A DslProxy should never be instantiated") + } +} diff --git a/java/kotlin/src/main/kotlin/com/google/protobuf/ExtendableMessageExtensions.kt b/java/kotlin/src/main/kotlin/com/google/protobuf/ExtendableMessageExtensions.kt new file mode 100644 index 00000000000..10630a93267 --- /dev/null +++ b/java/kotlin/src/main/kotlin/com/google/protobuf/ExtendableMessageExtensions.kt @@ -0,0 +1,26 @@ +package com.google.protobuf.kotlin + +import com.google.protobuf.ExtensionLite +import com.google.protobuf.GeneratedMessageV3 + +/** Sets the current value of the proto extension in this builder.*/ +operator fun < + M : GeneratedMessageV3.ExtendableMessage, + B : GeneratedMessageV3.ExtendableBuilder, + T : Any + > B.set(extension: ExtensionLite, value: T) { + setExtension(extension, value) +} + +/** Gets the current value of the proto extension. */ +operator fun < + M : GeneratedMessageV3.ExtendableMessage, + MorBT : GeneratedMessageV3.ExtendableMessageOrBuilder, + T : Any + > MorBT.get(extension: ExtensionLite): T = getExtension(extension) + +/** Returns true if the specified extension is set on this builder. */ +operator fun < + M : GeneratedMessageV3.ExtendableMessage, + MorBT : GeneratedMessageV3.ExtendableMessageOrBuilder + > MorBT.contains(extension: ExtensionLite): Boolean = hasExtension(extension) diff --git a/java/kotlin/src/main/kotlin/com/google/protobuf/ExtendableMessageLiteExtensions.kt b/java/kotlin/src/main/kotlin/com/google/protobuf/ExtendableMessageLiteExtensions.kt new file mode 100644 index 00000000000..dd1dc870d46 --- /dev/null +++ b/java/kotlin/src/main/kotlin/com/google/protobuf/ExtendableMessageLiteExtensions.kt @@ -0,0 +1,28 @@ +package com.google.protobuf.kotlin + +import com.google.protobuf.ExtensionLite +import com.google.protobuf.GeneratedMessageLite + +/** Gets the value of the proto extension. */ +operator fun < + M : GeneratedMessageLite.ExtendableMessage, + MOrBT : GeneratedMessageLite.ExtendableMessageOrBuilder, + T : Any + > MOrBT.get(extension: ExtensionLite): T = getExtension(extension) + +/** Sets the current value of the proto extension in this builder. */ +operator fun < + M : GeneratedMessageLite.ExtendableMessage, + B : GeneratedMessageLite.ExtendableBuilder, + T : Any + > B.set(extension: ExtensionLite, value: T) { + setExtension(extension, value) +} + +/** Returns true if the specified extension is set. */ +operator fun < + M : GeneratedMessageLite.ExtendableMessage, + MorBT : GeneratedMessageLite.ExtendableMessageOrBuilder + > MorBT.contains( + extension: ExtensionLite +): Boolean = hasExtension(extension) diff --git a/java/kotlin/src/main/kotlin/com/google/protobuf/ExtensionList.kt b/java/kotlin/src/main/kotlin/com/google/protobuf/ExtensionList.kt new file mode 100644 index 00000000000..03764e11787 --- /dev/null +++ b/java/kotlin/src/main/kotlin/com/google/protobuf/ExtensionList.kt @@ -0,0 +1,27 @@ +package com.google.protobuf.kotlin + +import com.google.protobuf.ExtensionLite +import com.google.protobuf.MessageLite + +/** + * Implementation for ExtensionList and ExtensionListLite. Like [DslList], represents an + * unmodifiable view of a repeated proto field -- in this case, an extension field -- but + * supports querying the extension. + */ +class ExtensionList @OnlyForUseByGeneratedProtoCode constructor( + val extension: ExtensionLite>, + @JvmField private val delegate: List +) : List by delegate { + override fun iterator(): Iterator = UnmodifiableIterator(delegate.iterator()) + + override fun listIterator(): ListIterator = UnmodifiableListIterator(delegate.listIterator()) + + override fun listIterator(index: Int): ListIterator = + UnmodifiableListIterator(delegate.listIterator(index)) + + override fun equals(other: Any?): Boolean = delegate == other + + override fun hashCode(): Int = delegate.hashCode() + + override fun toString(): String = delegate.toString() +} diff --git a/java/kotlin/src/main/kotlin/com/google/protobuf/OnlyForUseByGeneratedProtoCode.kt b/java/kotlin/src/main/kotlin/com/google/protobuf/OnlyForUseByGeneratedProtoCode.kt new file mode 100644 index 00000000000..368912ee2dc --- /dev/null +++ b/java/kotlin/src/main/kotlin/com/google/protobuf/OnlyForUseByGeneratedProtoCode.kt @@ -0,0 +1,18 @@ +package com.google.protobuf.kotlin + +/** + * Opt-in annotation to make it difficult to accidentally use APIs only intended for use by proto + * generated code. See https://kotlinlang.org/docs/reference/opt-in-requirements.html for details + * on how this API works. + */ +@RequiresOptIn( + message = + """ + This API is only intended for use by generated protobuf code, the code generator, and their own + tests. If this does not describe your code, you should not be using this API. + """, + level = RequiresOptIn.Level.ERROR +) +@Retention(AnnotationRetention.BINARY) +@Target(AnnotationTarget.CONSTRUCTOR, AnnotationTarget.ANNOTATION_CLASS) +annotation class OnlyForUseByGeneratedProtoCode diff --git a/java/kotlin/src/main/kotlin/com/google/protobuf/ProtoDslMarker.kt b/java/kotlin/src/main/kotlin/com/google/protobuf/ProtoDslMarker.kt new file mode 100644 index 00000000000..c51ba351aa9 --- /dev/null +++ b/java/kotlin/src/main/kotlin/com/google/protobuf/ProtoDslMarker.kt @@ -0,0 +1,10 @@ +package com.google.protobuf.kotlin + +/** + * Indicates an API that is part of a DSL to generate protocol buffer messages. + */ +@DslMarker +@Target(AnnotationTarget.CLASS) +@Retention(AnnotationRetention.BINARY) +@OnlyForUseByGeneratedProtoCode +annotation class ProtoDslMarker diff --git a/java/kotlin/src/main/kotlin/com/google/protobuf/UnmodifiableCollections.kt b/java/kotlin/src/main/kotlin/com/google/protobuf/UnmodifiableCollections.kt new file mode 100644 index 00000000000..6af93e1c00d --- /dev/null +++ b/java/kotlin/src/main/kotlin/com/google/protobuf/UnmodifiableCollections.kt @@ -0,0 +1,39 @@ +package com.google.protobuf.kotlin + +/** Wraps an [Iterator] and makes it unmodifiable even from Java. */ +internal class UnmodifiableIterator(delegate: Iterator) : Iterator by delegate + +/** Wraps a [ListIterator] and makes it unmodifiable even from Java. */ +internal class UnmodifiableListIterator( + delegate: ListIterator +) : ListIterator by delegate + +/** Wraps a [Collection] and makes it unmodifiable even from Java. */ +internal open class UnmodifiableCollection( + private val delegate: Collection +) : Collection by delegate { + override fun iterator(): Iterator = UnmodifiableIterator(delegate.iterator()) +} + +/** Wraps a [Set] and makes it unmodifiable even from Java. */ +internal class UnmodifiableSet( + delegate: Collection +) : UnmodifiableCollection(delegate), Set + +/** Wraps a [Map.Entry] and makes it unmodifiable even from Java. */ +internal class UnmodifiableMapEntry(delegate: Map.Entry) : Map.Entry by delegate + +/** Wraps a [Set] of map entries and makes it unmodifiable even from Java. */ +internal class UnmodifiableMapEntries( + private val delegate: Set> +) : UnmodifiableCollection>(delegate), Set> { + + // Is this overkill? Probably. + + override fun iterator(): Iterator> { + val itr = delegate.iterator() + return object : Iterator> by itr { + override fun next(): Map.Entry = UnmodifiableMapEntry(itr.next()) + } + } +} diff --git a/java/kotlin/src/test/kotlin/com/google/protobuf/DslListTest.kt b/java/kotlin/src/test/kotlin/com/google/protobuf/DslListTest.kt new file mode 100644 index 00000000000..e5e6017c062 --- /dev/null +++ b/java/kotlin/src/test/kotlin/com/google/protobuf/DslListTest.kt @@ -0,0 +1,98 @@ +package com.google.protobuf.kotlin + +import com.google.common.testing.EqualsTester +import com.google.common.truth.Truth.assertThat +import kotlin.test.assertFailsWith +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +/** Tests for [DslList]. */ +@RunWith(JUnit4::class) +@OptIn(OnlyForUseByGeneratedProtoCode::class) +class DslListTest { + class DummyProxy private constructor() : DslProxy() + + @Test + fun matchesList() { + assertThat(DslList(listOf(1, 2, 3))).containsExactly(1, 2, 3).inOrder() + } + + @Test + fun reflectsChangesInList() { + val mutableList = mutableListOf(1, 2, 3) + val dslList = DslList(mutableList) + mutableList.add(4) + assertThat(dslList).containsExactly(1, 2, 3, 4).inOrder() + } + + @Test + fun dslListIsNotMutable() { + val dslList = DslList(mutableListOf(1, 2, 3)) + assertThat(dslList is MutableList<*>).isFalse() + } + + @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "UNCHECKED_CAST") + @Test + fun dslListIsNotEvenSecretlyMutable() { + val dslList = DslList(mutableListOf(1, 2, 3)) + val dslListAsJavaUtil = dslList as java.util.List + assertFailsWith { + dslListAsJavaUtil.add(4) + } + } + + @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "UNCHECKED_CAST") + @Test + fun dslList_IteratorIsNotEvenSecretlyMutable() { + val dslList = DslList(mutableListOf(1, 2, 3)) + val iterator = dslList.iterator() as java.util.Iterator + iterator.next() + + assertFailsWith { + iterator.remove() + } + } + + @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "UNCHECKED_CAST") + @Test + fun dslList_ListIteratorIsNotEvenSecretlyMutable() { + val dslList = DslList(mutableListOf(1, 2, 3)) + val iterator = dslList.listIterator() as java.util.ListIterator + iterator.next() + + assertFailsWith { + iterator.remove() + } + } + + @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "UNCHECKED_CAST") + @Test + fun dslList_ListIteratorIndexIsNotEvenSecretlyMutable() { + val dslList = DslList(mutableListOf(1, 2, 3)) + val iterator = dslList.listIterator(1) as java.util.ListIterator + iterator.next() + + assertFailsWith { + iterator.remove() + } + } + + @Test + fun expectedToString() { + assertThat(DslList(listOf(1, 2)).toString()).isEqualTo("[1, 2]") + } + + @Test + fun equality() { + EqualsTester() + .addEqualityGroup(DslList(listOf(1, 2)), listOf(1, 2)) + .addEqualityGroup(DslList(listOf(2, 2)), listOf(2, 2)) + .addEqualityGroup( + DslList(emptyList()), + DslList(emptyList()), + emptyList() + ) + .testEquals() + } +} diff --git a/java/kotlin/src/test/kotlin/com/google/protobuf/DslMapTest.kt b/java/kotlin/src/test/kotlin/com/google/protobuf/DslMapTest.kt new file mode 100644 index 00000000000..470e42e48ac --- /dev/null +++ b/java/kotlin/src/test/kotlin/com/google/protobuf/DslMapTest.kt @@ -0,0 +1,164 @@ +package com.google.protobuf.kotlin + +import com.google.common.testing.EqualsTester +import com.google.common.truth.Truth.assertThat +import kotlin.test.assertFailsWith +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@RunWith(JUnit4::class) +@OptIn(OnlyForUseByGeneratedProtoCode::class) +class DslMapTest { + class DummyProxy private constructor() : DslProxy() + + @Test + fun matchesMap() { + assertThat(DslMap(mapOf(1 to -1, 2 to -2))) + .containsExactly(1, -1, 2, -2) + } + + @Test + fun reflectsChangesInMap() { + val mutableMap = mutableMapOf(1 to -1, 2 to -2) + val dslMap = DslMap(mutableMap) + mutableMap[3] = -3 + assertThat(dslMap).containsExactly(1, -1, 2, -2, 3, -3).inOrder() + } + + @Test + fun dslMapIsNotMutable() { + val dslMap = DslMap(mutableMapOf(1 to -1)) + assertThat(dslMap is MutableMap<*, *>).isFalse() + } + + @Test + fun dslMapKeysAreNotMutable() { + val dslMap = DslMap(mutableMapOf(1 to -1)) + assertThat(dslMap.keys is MutableSet<*>).isFalse() + } + + @Test + fun dslMapValuesAreNotMutable() { + val dslMap = DslMap(mutableMapOf(1 to -1)) + assertThat(dslMap.values is MutableSet<*>).isFalse() + } + + @Test + fun dslMapEntriesAreNotMutable() { + val dslMap = DslMap(mutableMapOf(1 to -1)) + assertThat(dslMap.entries is MutableSet<*>).isFalse() + } + + @Test + fun dslMapEntryObjectsAreNotMutable() { + val dslMap = DslMap(mutableMapOf(1 to -1)) + assertThat(dslMap.entries.single() is MutableMap.MutableEntry<*, *>).isFalse() + } + + @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "UNCHECKED_CAST") + @Test + fun dslMapIsNotEvenSecretlyMutable() { + val dslMap = DslMap(mutableMapOf(1 to -1)) + val dslMapAsJavaUtilMap = dslMap as java.util.Map + assertFailsWith { + dslMapAsJavaUtilMap.put(2, -2) + } + } + + @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "UNCHECKED_CAST") + @Test + fun dslMapKeysAreNotEvenSecretlyMutable() { + val dslMap = DslMap(mutableMapOf(1 to -1)) + val dslMapKeysAsJavaUtilSet = dslMap.keys as java.util.Set + assertFailsWith { + dslMapKeysAsJavaUtilSet.remove(1) + } + } + + @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "UNCHECKED_CAST") + @Test + fun dslMapKeysIteratorIsNotEvenSecretlyMutable() { + val dslMap = DslMap(mutableMapOf(1 to -1)) + val dslMapKeysAsJavaUtilSet = dslMap.keys as java.util.Set + val itr = dslMapKeysAsJavaUtilSet.iterator() + itr.next() + assertFailsWith { + itr.remove() + } + } + + @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "UNCHECKED_CAST") + @Test + fun dslMapValuesAreNotEvenSecretlyMutable() { + val dslMap = DslMap(mutableMapOf(1 to -1)) + val dslMapValuesAsJavaUtilCollection = dslMap.values as java.util.Collection + assertFailsWith { + dslMapValuesAsJavaUtilCollection.remove(1) + } + } + + @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "UNCHECKED_CAST") + @Test + fun dslMapValuesIteratorIsNotEvenSecretlyMutable() { + val dslMap = DslMap(mutableMapOf(1 to -1)) + val dslMapValuesAsJavaUtilCollection = dslMap.values as java.util.Collection + val itr = dslMapValuesAsJavaUtilCollection.iterator() + itr.next() + assertFailsWith { + itr.remove() + } + } + + @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "UNCHECKED_CAST") + @Test + fun dslMapEntriesAreNotEvenSecretlyMutable() { + val dslMap = DslMap(mutableMapOf(1 to -1)) + val dslMapEntriesAsJavaUtilSet = dslMap.entries as java.util.Set> + val entry = dslMap.entries.single() + assertFailsWith { + dslMapEntriesAsJavaUtilSet.remove(entry) + } + } + + @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "UNCHECKED_CAST") + @Test + fun dslMapEntriesIteratorIsNotEvenSecretlyMutable() { + val dslMap = DslMap(mutableMapOf(1 to -1)) + val dslMapEntriesAsJavaUtilSet = dslMap.entries as java.util.Set> + val itr = dslMapEntriesAsJavaUtilSet.iterator() + itr.next() + assertFailsWith { + itr.remove() + } + } + + @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "UNCHECKED_CAST") + @Test + fun dslMapEntryObjectsAreNotEvenSecretlyMutable() { + val dslMap = DslMap(mutableMapOf(1 to -1)) + val dslMapEntryAsJavaUtilMapEntry = dslMap.entries.single() as java.util.Map.Entry + assertFailsWith { + dslMapEntryAsJavaUtilMapEntry.value = 2 + } + } + + @Test + fun expectedToString() { + assertThat(DslMap(mapOf(1 to 2, 2 to 3)).toString()) + .isEqualTo("{1=2, 2=3}") + } + + @Test + fun equality() { + EqualsTester() + .addEqualityGroup(DslMap(mapOf(1 to 2, 2 to 3)), mapOf(1 to 2, 2 to 3)) + .addEqualityGroup(DslMap(mapOf(1 to 3, 2 to 3)), mapOf(1 to 3, 2 to 3)) + .addEqualityGroup( + DslMap(emptyMap()), + DslMap(emptyMap()), + emptyMap() + ) + .testEquals() + } +} diff --git a/java/kotlin/src/test/kotlin/com/google/protobuf/ExtendableMessageExtensionsTest.kt b/java/kotlin/src/test/kotlin/com/google/protobuf/ExtendableMessageExtensionsTest.kt new file mode 100644 index 00000000000..c8816eba7f4 --- /dev/null +++ b/java/kotlin/src/test/kotlin/com/google/protobuf/ExtendableMessageExtensionsTest.kt @@ -0,0 +1,60 @@ +package com.google.protobuf.kotlin + +import com.google.common.truth.Truth.assertThat +import example_extensible_message.ExampleExtensibleMessage +import example_extensible_message.ExampleExtensibleMessageOuterClass as TestProto +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@RunWith(JUnit4::class) +class ExtendableMessageExtensionsTest { + @Test + fun setOnBuilder() { + val builder = ExampleExtensibleMessage.newBuilder() + builder[TestProto.int32Extension] = 5 + assertThat(builder.build().getExtension(TestProto.int32Extension)).isEqualTo(5) + } + + @Test + fun getOnBuilder() { + val builder = ExampleExtensibleMessage.newBuilder() + .setExtension(TestProto.int32Extension, 6) + assertThat(builder[TestProto.int32Extension]).isEqualTo(6) + } + + @Test + fun getOnMessage() { + val message = ExampleExtensibleMessage.newBuilder() + .setExtension(TestProto.int32Extension, 6) + .build() + assertThat(message[TestProto.int32Extension]).isEqualTo(6) + } + + @Test + fun containsPositiveOnMessage() { + val message = ExampleExtensibleMessage.newBuilder() + .setExtension(TestProto.int32Extension, 6) + .build() + assertThat(TestProto.int32Extension in message).isTrue() + } + + @Test + fun containsPositiveOnBuilder() { + val builder = ExampleExtensibleMessage.newBuilder() + .setExtension(TestProto.int32Extension, 6) + assertThat(TestProto.int32Extension in builder).isTrue() + } + + @Test + fun containsNegativeOnMessage() { + val message = ExampleExtensibleMessage.newBuilder().build() + assertThat(TestProto.int32Extension in message).isFalse() + } + + @Test + fun containsNegativeOnBuilder() { + val builder = ExampleExtensibleMessage.newBuilder() + assertThat(TestProto.int32Extension in builder).isFalse() + } +} diff --git a/java/kotlin/src/test/kotlin/com/google/protobuf/ExtensionListTest.kt b/java/kotlin/src/test/kotlin/com/google/protobuf/ExtensionListTest.kt new file mode 100644 index 00000000000..e805e1e5517 --- /dev/null +++ b/java/kotlin/src/test/kotlin/com/google/protobuf/ExtensionListTest.kt @@ -0,0 +1,125 @@ +package com.google.protobuf.kotlin + +import com.google.common.testing.EqualsTester +import com.google.common.truth.Truth.assertThat +import example_extensible_message.ExampleExtensibleMessage +import example_extensible_message.ExampleExtensibleMessageOuterClass as TestProto +import kotlin.test.assertFailsWith +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +/** Tests for [DslList]. */ +@RunWith(JUnit4::class) +@OptIn(OnlyForUseByGeneratedProtoCode::class) +class ExtensionListTest { + class DummyProxy private constructor() : DslProxy() + + @Test + fun matchesList() { + assertThat( + ExtensionList( + TestProto.repeatedExtension, listOf(1, 2, 3) + ) + ).containsExactly(1, 2, 3).inOrder() + } + + @Test + fun reflectsChangesInList() { + val mutableList = mutableListOf(1, 2, 3) + val extensionList = ExtensionList( + TestProto.repeatedExtension, mutableList + ) + mutableList.add(4) + assertThat(extensionList).containsExactly(1, 2, 3, 4).inOrder() + } + + @Test + fun extensionListIsNotMutable() { + val extensionList = ExtensionList( + TestProto.repeatedExtension, mutableListOf(1, 2, 3) + ) + assertThat(extensionList is MutableList<*>).isFalse() + } + + @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "UNCHECKED_CAST") + @Test + fun extensionListIsNotEvenSecretlyMutable() { + val extensionList = ExtensionList( + TestProto.repeatedExtension, mutableListOf(1, 2, 3) + ) + val extensionListAsJavaUtil = extensionList as java.util.List + assertFailsWith { + extensionListAsJavaUtil.add(4) + } + } + + @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "UNCHECKED_CAST") + @Test + fun extensionList_IteratorIsNotEvenSecretlyMutable() { + val extensionList = ExtensionList( + TestProto.repeatedExtension, mutableListOf(1, 2, 3) + ) + val iterator = extensionList.iterator() as java.util.Iterator + iterator.next() + + assertFailsWith { + iterator.remove() + } + } + + @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "UNCHECKED_CAST") + @Test + fun extensionList_ListIteratorIsNotEvenSecretlyMutable() { + val extensionList = ExtensionList( + TestProto.repeatedExtension, mutableListOf(1, 2, 3) + ) + val iterator = extensionList.listIterator() as java.util.ListIterator + iterator.next() + + assertFailsWith { + iterator.remove() + } + } + + @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "UNCHECKED_CAST") + @Test + fun extensionList_ListIteratorIndexIsNotEvenSecretlyMutable() { + val extensionList = ExtensionList( + TestProto.repeatedExtension, mutableListOf(1, 2, 3) + ) + val iterator = extensionList.listIterator(1) as java.util.ListIterator + iterator.next() + + assertFailsWith { + iterator.remove() + } + } + + @Test + fun expectedToString() { + assertThat( + ExtensionList(TestProto.repeatedExtension, listOf(1, 2)) + .toString() + ).isEqualTo("[1, 2]") + } + + @Test + fun equality() { + EqualsTester() + .addEqualityGroup( + ExtensionList(TestProto.repeatedExtension, listOf(1, 2)), + ExtensionList(TestProto.differentExtension, listOf(1, 2)), + listOf(1, 2) + ) + .addEqualityGroup( + ExtensionList(TestProto.repeatedExtension, listOf(2, 2)), + listOf(2, 2) + ) + .addEqualityGroup( + ExtensionList(TestProto.repeatedExtension, emptyList()), + emptyList() + ) + .testEquals() + } +} diff --git a/java/kotlin/src/test/kotlin/com/google/protobuf/Proto2Test.kt b/java/kotlin/src/test/kotlin/com/google/protobuf/Proto2Test.kt new file mode 100644 index 00000000000..72b9792b55f --- /dev/null +++ b/java/kotlin/src/test/kotlin/com/google/protobuf/Proto2Test.kt @@ -0,0 +1,891 @@ +package com.google.protobuf.kotlin + +import com.google.common.truth.Truth.assertThat +import com.google.protobuf.TestUtil +import com.google.protobuf.TestUtil.toBytes +import evil_names_proto2.EvilNamesProto2OuterClass.EvilNamesProto2 +import evil_names_proto2.EvilNamesProto2OuterClass.HardKeywordsAllTypes +import evil_names_proto2.EvilNamesProto2OuterClass.Interface +import evil_names_proto2.HardKeywordsAllTypesKt +import evil_names_proto2.evilNamesProto2 +import evil_names_proto2.hardKeywordsAllTypes +import evil_names_proto2.interface_ +import com.google.protobuf.test.UnittestImport.ImportEnum +import com.google.protobuf.test.UnittestImport.ImportMessage +import com.google.protobuf.test.UnittestImportPublic.PublicImportMessage +import protobuf_unittest.MapProto2Unittest.Proto2MapEnum +import protobuf_unittest.MapProto2Unittest.TestEnumMap +import protobuf_unittest.MapProto2Unittest.TestIntIntMap +import protobuf_unittest.MapProto2Unittest.TestMaps +import protobuf_unittest.TestAllTypesKt +import protobuf_unittest.TestAllTypesKt.nestedMessage +import protobuf_unittest.UnittestProto +import protobuf_unittest.UnittestProto.ForeignEnum +import protobuf_unittest.UnittestProto.TestAllTypes +import protobuf_unittest.UnittestProto.TestAllTypes.NestedEnum +import protobuf_unittest.UnittestProto.TestEmptyMessage +import protobuf_unittest.UnittestProto.TestEmptyMessageWithExtensions +import protobuf_unittest.copy +import protobuf_unittest.foreignMessage +import protobuf_unittest.optionalGroupExtension +import protobuf_unittest.repeatedGroupExtension +import protobuf_unittest.testAllExtensions +import protobuf_unittest.testAllTypes +import protobuf_unittest.testEmptyMessage +import protobuf_unittest.testEmptyMessageWithExtensions +import protobuf_unittest.testEnumMap +import protobuf_unittest.testIntIntMap +import protobuf_unittest.testMaps +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@RunWith(JUnit4::class) +class Proto2Test { + @Test + fun testSetters() { + assertThat( + testAllTypes { + optionalInt32 = 101 + optionalInt64 = 102 + optionalUint32 = 103 + optionalUint64 = 104 + optionalSint32 = 105 + optionalSint64 = 106 + optionalFixed32 = 107 + optionalFixed64 = 108 + optionalSfixed32 = 109 + optionalSfixed64 = 110 + optionalFloat = 111.0f + optionalDouble = 112.0 + optionalBool = true + optionalString = "115" + optionalBytes = toBytes("116") + optionalGroup = + TestAllTypesKt.optionalGroup { a = 117 } + optionalNestedMessage = nestedMessage { bb = 118 } + optionalForeignMessage = foreignMessage { c = 119 } + optionalImportMessage = + ImportMessage.newBuilder().setD(120).build() + optionalPublicImportMessage = + PublicImportMessage.newBuilder().setE(126).build() + optionalLazyMessage = nestedMessage { bb = 127 } + optionalNestedEnum = NestedEnum.BAZ + optionalForeignEnum = ForeignEnum.FOREIGN_BAZ + optionalImportEnum = ImportEnum.IMPORT_BAZ + optionalStringPiece = "124" + optionalCord = "125" + repeatedInt32.add(201) + repeatedInt64.add(202) + repeatedUint32.add(203) + repeatedUint64.add(204) + repeatedSint32.add(205) + repeatedSint64.add(206) + repeatedFixed32.add(207) + repeatedFixed64.add(208) + repeatedSfixed32.add(209) + repeatedSfixed64.add(210) + repeatedFloat.add(211f) + repeatedDouble.add(212.0) + repeatedBool.add(true) + repeatedString.add("215") + repeatedBytes.add(toBytes("216")) + repeatedGroup.add(TestAllTypesKt.repeatedGroup { a = 217 }) + repeatedNestedMessage.add(nestedMessage { bb = 218 }) + repeatedForeignMessage.add(foreignMessage { c = 219 }) + repeatedImportMessage.add( + ImportMessage.newBuilder().setD(220).build() + ) + repeatedLazyMessage.add(nestedMessage { bb = 227 }) + repeatedNestedEnum.add(NestedEnum.BAR) + repeatedForeignEnum.add(ForeignEnum.FOREIGN_BAR) + repeatedImportEnum.add(ImportEnum.IMPORT_BAR) + repeatedStringPiece.add("224") + repeatedCord.add("225") + repeatedInt32 += 301 + repeatedInt64 += 302 + repeatedUint32 += 303 + repeatedUint64 += 304 + repeatedSint32 += 305 + repeatedSint64 += 306 + repeatedFixed32 += 307 + repeatedFixed64 += 308 + repeatedSfixed32 += 309 + repeatedSfixed64 += 310 + repeatedFloat += 311f + repeatedDouble += 312.0 + repeatedBool += false + repeatedString += "315" + repeatedBytes += toBytes("316") + repeatedGroup += TestAllTypesKt.repeatedGroup { a = 317 } + repeatedNestedMessage += nestedMessage { bb = 318 } + repeatedForeignMessage += foreignMessage { c = 319 } + repeatedImportMessage += + ImportMessage.newBuilder().setD(320).build() + repeatedLazyMessage += + TestAllTypesKt.nestedMessage { bb = 327 } + repeatedNestedEnum += NestedEnum.BAZ + repeatedForeignEnum += ForeignEnum.FOREIGN_BAZ + repeatedImportEnum += ImportEnum.IMPORT_BAZ + repeatedStringPiece += "324" + repeatedCord += "325" + defaultInt32 = 401 + defaultInt64 = 402 + defaultUint32 = 403 + defaultUint64 = 404 + defaultSint32 = 405 + defaultSint64 = 406 + defaultFixed32 = 407 + defaultFixed64 = 408 + defaultSfixed32 = 409 + defaultSfixed64 = 410 + defaultFloat = 411f + defaultDouble = 412.0 + defaultBool = false + defaultString = "415" + defaultBytes = toBytes("416") + defaultNestedEnum = NestedEnum.FOO + defaultForeignEnum = ForeignEnum.FOREIGN_FOO + defaultImportEnum = ImportEnum.IMPORT_FOO + defaultStringPiece = "424" + defaultCord = "425" + oneofUint32 = 601 + oneofNestedMessage = + TestAllTypesKt.nestedMessage { bb = 602 } + oneofString = "603" + oneofBytes = toBytes("604") + } + ).isEqualTo( + TestUtil.getAllSetBuilder().build() + ) + } + + @Test + fun testGetters() { + testAllTypes { + optionalInt32 = 101 + assertThat(optionalInt32).isEqualTo(101) + optionalString = "115" + assertThat(optionalString).isEqualTo("115") + optionalGroup = TestAllTypesKt.optionalGroup { a = 117 } + assertThat(optionalGroup).isEqualTo(TestAllTypesKt.optionalGroup { a = 117 }) + optionalNestedMessage = TestAllTypesKt.nestedMessage { bb = 118 } + assertThat(optionalNestedMessage).isEqualTo(TestAllTypesKt.nestedMessage { bb = 118 }) + optionalNestedEnum = NestedEnum.BAZ + assertThat(optionalNestedEnum).isEqualTo(NestedEnum.BAZ) + defaultInt32 = 401 + assertThat(defaultInt32).isEqualTo(401) + oneofUint32 = 601 + assertThat(oneofUint32).isEqualTo(601) + } + } + + @Test + fun testDefaultGetters() { + testAllTypes { + assertThat(defaultInt32).isEqualTo(41) + assertThat(defaultString).isEqualTo("hello") + assertThat(defaultNestedEnum).isEqualTo(NestedEnum.BAR) + assertThat(defaultStringPiece).isEqualTo("abc") + } + } + + @Test + fun testRepeatedGettersAndSetters() { + testAllTypes { + repeatedInt32.addAll(listOf(1, 2)) + assertThat(repeatedInt32).isEqualTo(listOf(1, 2)) + repeatedInt32 += listOf(3, 4) + assertThat(repeatedInt32).isEqualTo(listOf(1, 2, 3, 4)) + repeatedInt32[0] = 5 + assertThat(repeatedInt32).isEqualTo(listOf(5, 2, 3, 4)) + + repeatedString.addAll(listOf("1", "2")) + assertThat(repeatedString).isEqualTo(listOf("1", "2")) + repeatedString += listOf("3", "4") + assertThat(repeatedString).isEqualTo(listOf("1", "2", "3", "4")) + repeatedString[0] = "5" + assertThat(repeatedString).isEqualTo(listOf("5", "2", "3", "4")) + + repeatedGroup.addAll( + listOf( + TestAllTypesKt.repeatedGroup { a = 1 }, + TestAllTypesKt.repeatedGroup { a = 2 } + ) + ) + assertThat(repeatedGroup).isEqualTo( + listOf( + TestAllTypesKt.repeatedGroup { a = 1 }, + TestAllTypesKt.repeatedGroup { a = 2 } + ) + ) + repeatedGroup += + listOf( + TestAllTypesKt.repeatedGroup { a = 3 }, + TestAllTypesKt.repeatedGroup { a = 4 } + ) + assertThat(repeatedGroup).isEqualTo( + listOf( + TestAllTypesKt.repeatedGroup { a = 1 }, + TestAllTypesKt.repeatedGroup { a = 2 }, + TestAllTypesKt.repeatedGroup { a = 3 }, + TestAllTypesKt.repeatedGroup { a = 4 } + ) + ) + repeatedGroup[0] = TestAllTypesKt.repeatedGroup { a = 5 } + assertThat(repeatedGroup).isEqualTo( + listOf( + TestAllTypesKt.repeatedGroup { a = 5 }, + TestAllTypesKt.repeatedGroup { a = 2 }, + TestAllTypesKt.repeatedGroup { a = 3 }, + TestAllTypesKt.repeatedGroup { a = 4 } + ) + ) + + repeatedNestedMessage.addAll(listOf(nestedMessage { bb = 1 }, nestedMessage { bb = 2 })) + assertThat(repeatedNestedMessage).isEqualTo( + listOf( + nestedMessage { bb = 1 }, + nestedMessage { bb = 2 } + ) + ) + repeatedNestedMessage += listOf(nestedMessage { bb = 3 }, nestedMessage { bb = 4 }) + assertThat(repeatedNestedMessage).isEqualTo( + listOf( + nestedMessage { bb = 1 }, + nestedMessage { bb = 2 }, + nestedMessage { bb = 3 }, + nestedMessage { bb = 4 } + ) + ) + repeatedNestedMessage[0] = nestedMessage { bb = 5 } + assertThat(repeatedNestedMessage).isEqualTo( + listOf( + nestedMessage { bb = 5 }, + nestedMessage { bb = 2 }, + nestedMessage { bb = 3 }, + nestedMessage { bb = 4 } + ) + ) + + repeatedNestedEnum.addAll(listOf(NestedEnum.FOO, NestedEnum.BAR)) + assertThat(repeatedNestedEnum).isEqualTo(listOf(NestedEnum.FOO, NestedEnum.BAR)) + repeatedNestedEnum += listOf(NestedEnum.BAZ, NestedEnum.FOO) + assertThat(repeatedNestedEnum).isEqualTo( + listOf(NestedEnum.FOO, NestedEnum.BAR, NestedEnum.BAZ, NestedEnum.FOO) + ) + repeatedNestedEnum[0] = NestedEnum.BAR + assertThat(repeatedNestedEnum).isEqualTo( + listOf(NestedEnum.BAR, NestedEnum.BAR, NestedEnum.BAZ, NestedEnum.FOO) + ) + } + } + + @Test + fun testHazzers() { + testAllTypes { + optionalInt32 = 101 + assertThat(hasOptionalInt32()).isTrue() + assertThat(hasOptionalString()).isFalse() + optionalGroup = TestAllTypesKt.optionalGroup { a = 117 } + assertThat(hasOptionalGroup()).isTrue() + assertThat(hasOptionalNestedMessage()).isFalse() + optionalNestedEnum = NestedEnum.BAZ + assertThat(hasOptionalNestedEnum()).isTrue() + assertThat(hasDefaultInt32()).isFalse() + oneofUint32 = 601 + assertThat(hasOneofUint32()).isTrue() + } + + testAllTypes { + assertThat(hasOptionalInt32()).isFalse() + optionalString = "115" + assertThat(hasOptionalString()).isTrue() + assertThat(hasOptionalGroup()).isFalse() + optionalNestedMessage = TestAllTypesKt.nestedMessage { bb = 118 } + assertThat(hasOptionalNestedMessage()).isTrue() + assertThat(hasOptionalNestedEnum()).isFalse() + defaultInt32 = 401 + assertThat(hasDefaultInt32()).isTrue() + assertThat(hasOneofUint32()).isFalse() + } + } + + @Test + fun testClears() { + testAllTypes { + optionalInt32 = 101 + clearOptionalInt32() + assertThat(hasOptionalInt32()).isFalse() + + optionalString = "115" + clearOptionalString() + assertThat(hasOptionalString()).isFalse() + + optionalGroup = TestAllTypesKt.optionalGroup { a = 117 } + clearOptionalGroup() + assertThat(hasOptionalGroup()).isFalse() + + optionalNestedMessage = TestAllTypesKt.nestedMessage { bb = 118 } + clearOptionalNestedMessage() + assertThat(hasOptionalNestedMessage()).isFalse() + + optionalNestedEnum = NestedEnum.BAZ + clearOptionalNestedEnum() + assertThat(hasOptionalNestedEnum()).isFalse() + + defaultInt32 = 401 + clearDefaultInt32() + assertThat(hasDefaultInt32()).isFalse() + + oneofUint32 = 601 + clearOneofUint32() + assertThat(hasOneofUint32()).isFalse() + } + } + + @Test + fun testCopy() { + val message = testAllTypes { + optionalInt32 = 101 + optionalString = "115" + } + val modifiedMessage = message.copy { + optionalInt32 = 201 + } + + assertThat(message).isEqualTo( + TestAllTypes.newBuilder() + .setOptionalInt32(101) + .setOptionalString("115") + .build() + ) + assertThat(modifiedMessage).isEqualTo( + TestAllTypes.newBuilder() + .setOptionalInt32(201) + .setOptionalString("115") + .build() + ) + } + + @Test + fun testOneof() { + val message = testAllTypes { + oneofString = "foo" + assertThat(oneofFieldCase) + .isEqualTo(TestAllTypes.OneofFieldCase.ONEOF_STRING) + assertThat(oneofString).isEqualTo("foo") + clearOneofField() + assertThat(hasOneofUint32()).isFalse() + assertThat(oneofFieldCase) + .isEqualTo(TestAllTypes.OneofFieldCase.ONEOFFIELD_NOT_SET) + oneofUint32 = 5 + } + + assertThat(message.getOneofFieldCase()) + .isEqualTo(TestAllTypes.OneofFieldCase.ONEOF_UINT32) + assertThat(message.getOneofUint32()).isEqualTo(5) + } + + @Test + fun testExtensionsSet() { + assertThat( + testAllExtensions { + this[UnittestProto.optionalInt32Extension] = 101 + this[UnittestProto.optionalInt64Extension] = 102L + this[UnittestProto.optionalUint32Extension] = 103 + this[UnittestProto.optionalUint64Extension] = 104L + this[UnittestProto.optionalSint32Extension] = 105 + this[UnittestProto.optionalSint64Extension] = 106L + this[UnittestProto.optionalFixed32Extension] = 107 + this[UnittestProto.optionalFixed64Extension] = 108L + this[UnittestProto.optionalSfixed32Extension] = 109 + this[UnittestProto.optionalSfixed64Extension] = 110L + this[UnittestProto.optionalFloatExtension] = 111F + this[UnittestProto.optionalDoubleExtension] = 112.0 + this[UnittestProto.optionalBoolExtension] = true + this[UnittestProto.optionalStringExtension] = "115" + this[UnittestProto.optionalBytesExtension] = toBytes("116") + this[UnittestProto.optionalGroupExtension] = optionalGroupExtension { a = 117 } + this[UnittestProto.optionalNestedMessageExtension] = + TestAllTypesKt.nestedMessage { bb = 118 } + this[UnittestProto.optionalForeignMessageExtension] = foreignMessage { c = 119 } + this[UnittestProto.optionalImportMessageExtension] = + ImportMessage.newBuilder().setD(120).build() + this[UnittestProto.optionalPublicImportMessageExtension] = + PublicImportMessage.newBuilder().setE(126).build() + this[UnittestProto.optionalLazyMessageExtension] = TestAllTypesKt.nestedMessage { bb = 127 } + this[UnittestProto.optionalNestedEnumExtension] = NestedEnum.BAZ + this[UnittestProto.optionalForeignEnumExtension] = ForeignEnum.FOREIGN_BAZ + this[UnittestProto.optionalImportEnumExtension] = ImportEnum.IMPORT_BAZ + this[UnittestProto.optionalStringPieceExtension] = "124" + this[UnittestProto.optionalCordExtension] = "125" + this[UnittestProto.repeatedInt32Extension].add(201) + this[UnittestProto.repeatedInt64Extension].add(202L) + this[UnittestProto.repeatedUint32Extension].add(203) + this[UnittestProto.repeatedUint64Extension].add(204L) + this[UnittestProto.repeatedSint32Extension].add(205) + this[UnittestProto.repeatedSint64Extension].add(206L) + this[UnittestProto.repeatedFixed32Extension].add(207) + this[UnittestProto.repeatedFixed64Extension].add(208L) + this[UnittestProto.repeatedSfixed32Extension].add(209) + this[UnittestProto.repeatedSfixed64Extension].add(210L) + this[UnittestProto.repeatedFloatExtension].add(211F) + this[UnittestProto.repeatedDoubleExtension].add(212.0) + this[UnittestProto.repeatedBoolExtension].add(true) + this[UnittestProto.repeatedStringExtension].add("215") + this[UnittestProto.repeatedBytesExtension].add(toBytes("216")) + this[UnittestProto.repeatedGroupExtension].add(repeatedGroupExtension { a = 217 }) + this[UnittestProto.repeatedNestedMessageExtension] + .add(TestAllTypesKt.nestedMessage { bb = 218 }) + this[UnittestProto.repeatedForeignMessageExtension].add(foreignMessage { c = 219 }) + this[UnittestProto.repeatedImportMessageExtension] + .add(ImportMessage.newBuilder().setD(220).build()) + this[UnittestProto.repeatedLazyMessageExtension] + .add(TestAllTypesKt.nestedMessage { bb = 227 }) + this[UnittestProto.repeatedNestedEnumExtension].add(NestedEnum.BAR) + this[UnittestProto.repeatedForeignEnumExtension].add(ForeignEnum.FOREIGN_BAR) + this[UnittestProto.repeatedImportEnumExtension].add(ImportEnum.IMPORT_BAR) + this[UnittestProto.repeatedStringPieceExtension].add("224") + this[UnittestProto.repeatedCordExtension].add("225") + this[UnittestProto.repeatedInt32Extension].add(301) + this[UnittestProto.repeatedInt64Extension].add(302L) + this[UnittestProto.repeatedUint32Extension].add(303) + this[UnittestProto.repeatedUint64Extension].add(304L) + this[UnittestProto.repeatedSint32Extension].add(305) + this[UnittestProto.repeatedSint64Extension].add(306L) + this[UnittestProto.repeatedFixed32Extension].add(307) + this[UnittestProto.repeatedFixed64Extension].add(308L) + this[UnittestProto.repeatedSfixed32Extension].add(309) + this[UnittestProto.repeatedSfixed64Extension].add(310L) + this[UnittestProto.repeatedFloatExtension].add(311F) + this[UnittestProto.repeatedDoubleExtension].add(312.0) + this[UnittestProto.repeatedBoolExtension].add(false) + this[UnittestProto.repeatedStringExtension].add("315") + this[UnittestProto.repeatedBytesExtension].add(toBytes("316")) + this[UnittestProto.repeatedGroupExtension].add(repeatedGroupExtension { a = 317 }) + this[UnittestProto.repeatedNestedMessageExtension] + .add(TestAllTypesKt.nestedMessage { bb = 318 }) + this[UnittestProto.repeatedForeignMessageExtension].add(foreignMessage { c = 319 }) + this[UnittestProto.repeatedImportMessageExtension] + .add(ImportMessage.newBuilder().setD(320).build()) + this[UnittestProto.repeatedLazyMessageExtension] + .add(TestAllTypesKt.nestedMessage { bb = 327 }) + this[UnittestProto.repeatedNestedEnumExtension].add(NestedEnum.BAZ) + this[UnittestProto.repeatedForeignEnumExtension].add(ForeignEnum.FOREIGN_BAZ) + this[UnittestProto.repeatedImportEnumExtension].add(ImportEnum.IMPORT_BAZ) + this[UnittestProto.repeatedStringPieceExtension].add("324") + this[UnittestProto.repeatedCordExtension].add("325") + this[UnittestProto.defaultInt32Extension] = 401 + this[UnittestProto.defaultInt64Extension] = 402L + this[UnittestProto.defaultUint32Extension] = 403 + this[UnittestProto.defaultUint64Extension] = 404L + this[UnittestProto.defaultSint32Extension] = 405 + this[UnittestProto.defaultSint64Extension] = 406L + this[UnittestProto.defaultFixed32Extension] = 407 + this[UnittestProto.defaultFixed64Extension] = 408L + this[UnittestProto.defaultSfixed32Extension] = 409 + this[UnittestProto.defaultSfixed64Extension] = 410L + this[UnittestProto.defaultFloatExtension] = 411F + this[UnittestProto.defaultDoubleExtension] = 412.0 + this[UnittestProto.defaultBoolExtension] = false + this[UnittestProto.defaultStringExtension] = "415" + this[UnittestProto.defaultBytesExtension] = toBytes("416") + this[UnittestProto.defaultNestedEnumExtension] = NestedEnum.FOO + this[UnittestProto.defaultForeignEnumExtension] = ForeignEnum.FOREIGN_FOO + this[UnittestProto.defaultImportEnumExtension] = ImportEnum.IMPORT_FOO + this[UnittestProto.defaultStringPieceExtension] = "424" + this[UnittestProto.defaultCordExtension] = "425" + this[UnittestProto.oneofUint32Extension] = 601 + this[UnittestProto.oneofNestedMessageExtension] = TestAllTypesKt.nestedMessage { bb = 602 } + this[UnittestProto.oneofStringExtension] = "603" + this[UnittestProto.oneofBytesExtension] = toBytes("604") + } + ).isEqualTo( + TestUtil.getAllExtensionsSet() + ) + } + + @Test + fun testExtensionGetters() { + testAllExtensions { + this[UnittestProto.optionalInt32Extension] = 101 + assertThat(this[UnittestProto.optionalInt32Extension]).isEqualTo(101) + this[UnittestProto.optionalStringExtension] = "115" + assertThat(this[UnittestProto.optionalStringExtension]).isEqualTo("115") + this[UnittestProto.optionalGroupExtension] = optionalGroupExtension { a = 117 } + assertThat(this[UnittestProto.optionalGroupExtension]) + .isEqualTo(optionalGroupExtension { a = 117 }) + this[UnittestProto.optionalNestedMessageExtension] = + TestAllTypesKt.nestedMessage { bb = 118 } + assertThat(this[UnittestProto.optionalNestedMessageExtension]) + .isEqualTo(TestAllTypesKt.nestedMessage { bb = 118 }) + this[UnittestProto.optionalNestedEnumExtension] = NestedEnum.BAZ + assertThat(this[UnittestProto.optionalNestedEnumExtension]).isEqualTo(NestedEnum.BAZ) + this[UnittestProto.defaultInt32Extension] = 401 + assertThat(this[UnittestProto.defaultInt32Extension]).isEqualTo(401) + this[UnittestProto.oneofUint32Extension] = 601 + assertThat(this[UnittestProto.oneofUint32Extension]).isEqualTo(601) + } + } + + @Test + fun testExtensionContains() { + testAllExtensions { + this[UnittestProto.optionalInt32Extension] = 101 + assertThat(contains(UnittestProto.optionalInt32Extension)).isTrue() + assertThat(contains(UnittestProto.optionalStringExtension)).isFalse() + this[UnittestProto.optionalGroupExtension] = optionalGroupExtension { a = 117 } + assertThat(contains(UnittestProto.optionalGroupExtension)).isTrue() + assertThat(contains(UnittestProto.optionalNestedMessageExtension)).isFalse() + this[UnittestProto.optionalNestedEnumExtension] = NestedEnum.BAZ + assertThat(contains(UnittestProto.optionalNestedEnumExtension)).isTrue() + assertThat(contains(UnittestProto.defaultInt32Extension)).isFalse() + this[UnittestProto.oneofUint32Extension] = 601 + assertThat(contains(UnittestProto.optionalInt32Extension)).isTrue() + } + + testAllExtensions { + assertThat(contains(UnittestProto.optionalInt32Extension)).isFalse() + this[UnittestProto.optionalStringExtension] = "115" + assertThat(contains(UnittestProto.optionalStringExtension)).isTrue() + assertThat(contains(UnittestProto.optionalGroupExtension)).isFalse() + this[UnittestProto.optionalNestedMessageExtension] = + TestAllTypesKt.nestedMessage { bb = 118 } + assertThat(contains(UnittestProto.optionalNestedMessageExtension)).isTrue() + assertThat(contains(UnittestProto.optionalNestedEnumExtension)).isFalse() + this[UnittestProto.defaultInt32Extension] = 401 + assertThat(contains(UnittestProto.defaultInt32Extension)).isTrue() + assertThat(contains(UnittestProto.oneofUint32Extension)).isFalse() + } + } + + @Test + fun testExtensionClears() { + testAllExtensions { + this[UnittestProto.optionalInt32Extension] = 101 + clear(UnittestProto.optionalInt32Extension) + assertThat(contains(UnittestProto.optionalInt32Extension)).isFalse() + + this[UnittestProto.optionalStringExtension] = "115" + clear(UnittestProto.optionalStringExtension) + assertThat(contains(UnittestProto.optionalStringExtension)).isFalse() + + this[UnittestProto.optionalGroupExtension] = optionalGroupExtension { a = 117 } + clear(UnittestProto.optionalGroupExtension) + assertThat(contains(UnittestProto.optionalGroupExtension)).isFalse() + + this[UnittestProto.optionalNestedMessageExtension] = + TestAllTypesKt.nestedMessage { bb = 118 } + clear(UnittestProto.optionalNestedMessageExtension) + assertThat(contains(UnittestProto.optionalNestedMessageExtension)).isFalse() + + this[UnittestProto.optionalNestedEnumExtension] = NestedEnum.BAZ + clear(UnittestProto.optionalNestedEnumExtension) + assertThat(contains(UnittestProto.optionalNestedEnumExtension)).isFalse() + + this[UnittestProto.defaultInt32Extension] = 401 + clear(UnittestProto.defaultInt32Extension) + assertThat(contains(UnittestProto.oneofUint32Extension)).isFalse() + } + } + + @Test + fun testEmptyMessages() { + assertThat( + testEmptyMessage {} + ).isEqualTo( + TestEmptyMessage.newBuilder().build() + ) + + assertThat( + testEmptyMessageWithExtensions {} + ).isEqualTo( + TestEmptyMessageWithExtensions.newBuilder().build() + ) + } + + @Test + fun testMapSetters() { + val intMap = testIntIntMap { m[1] = 2 } + assertThat(intMap).isEqualTo( + TestIntIntMap.newBuilder().putM(1, 2).build() + ) + + assertThat( + testMaps { + mInt32[1] = intMap + mInt64[1L] = intMap + mUint32[1] = intMap + mUint64[1L] = intMap + mSint32[1] = intMap + mSint64[1L] = intMap + mFixed32[1] = intMap + mFixed64[1L] = intMap + mSfixed32[1] = intMap + mSfixed64[1] = intMap + mBool[true] = intMap + mString["1"] = intMap + } + ).isEqualTo( + TestMaps.newBuilder() + .putMInt32(1, intMap) + .putMInt64(1L, intMap) + .putMUint32(1, intMap) + .putMUint64(1L, intMap) + .putMSint32(1, intMap) + .putMSint64(1L, intMap) + .putMFixed32(1, intMap) + .putMFixed64(1L, intMap) + .putMSfixed32(1, intMap) + .putMSfixed64(1L, intMap) + .putMBool(true, intMap) + .putMString("1", intMap) + .build() + ) + + assertThat( + testEnumMap { + knownMapField[1] = Proto2MapEnum.PROTO2_MAP_ENUM_FOO + } + ).isEqualTo( + TestEnumMap.newBuilder() + .putKnownMapField(1, Proto2MapEnum.PROTO2_MAP_ENUM_FOO) + .build() + ) + } + + @Test + fun testMapGettersAndSetters() { + val intMap = + testIntIntMap { + m.put(1, 2) + assertThat(m).isEqualTo(mapOf(1 to 2)) + m[3] = 4 + assertThat(m).isEqualTo(mapOf(1 to 2, 3 to 4)) + m.putAll(mapOf(5 to 6, 7 to 8)) + assertThat(m).isEqualTo(mapOf(1 to 2, 3 to 4, 5 to 6, 7 to 8)) + } + + testMaps { + mInt32.put(1, intMap) + assertThat(mInt32).isEqualTo(mapOf(1 to intMap)) + mInt32[2] = intMap + assertThat(mInt32).isEqualTo(mapOf(1 to intMap, 2 to intMap)) + mInt32.putAll(mapOf(3 to intMap, 4 to intMap)) + assertThat(mInt32).isEqualTo(mapOf(1 to intMap, 2 to intMap, 3 to intMap, 4 to intMap)) + + mString.put("1", intMap) + assertThat(mString).isEqualTo(mapOf("1" to intMap)) + mString["2"] = intMap + assertThat(mString).isEqualTo(mapOf("1" to intMap, "2" to intMap)) + mString.putAll(mapOf("3" to intMap, "4" to intMap)) + assertThat(mString).isEqualTo( + mapOf("1" to intMap, "2" to intMap, "3" to intMap, "4" to intMap) + ) + } + + testEnumMap { + knownMapField.put(1, Proto2MapEnum.PROTO2_MAP_ENUM_FOO) + assertThat(knownMapField).isEqualTo(mapOf(1 to Proto2MapEnum.PROTO2_MAP_ENUM_FOO)) + knownMapField[2] = Proto2MapEnum.PROTO2_MAP_ENUM_BAR + assertThat(knownMapField).isEqualTo( + mapOf(1 to Proto2MapEnum.PROTO2_MAP_ENUM_FOO, 2 to Proto2MapEnum.PROTO2_MAP_ENUM_BAR) + ) + knownMapField.putAll( + mapOf(3 to Proto2MapEnum.PROTO2_MAP_ENUM_BAZ, 4 to Proto2MapEnum.PROTO2_MAP_ENUM_FOO) + ) + assertThat(knownMapField).isEqualTo( + mapOf( + 1 to Proto2MapEnum.PROTO2_MAP_ENUM_FOO, + 2 to Proto2MapEnum.PROTO2_MAP_ENUM_BAR, + 3 to Proto2MapEnum.PROTO2_MAP_ENUM_BAZ, + 4 to Proto2MapEnum.PROTO2_MAP_ENUM_FOO + ) + ) + } + } + + @Test + fun testMapRemove() { + val intMap = + testIntIntMap { + m.putAll(mapOf(1 to 2, 3 to 4)) + m.remove(1) + assertThat(m).isEqualTo(mapOf(3 to 4)) + } + + testMaps { + mInt32.putAll(mapOf(1 to intMap, 2 to intMap)) + mInt32.remove(1) + assertThat(mInt32).isEqualTo(mapOf(2 to intMap)) + + mString.putAll(mapOf("1" to intMap, "2" to intMap)) + mString.remove("1") + assertThat(mString).isEqualTo(mapOf("2" to intMap)) + } + + testEnumMap { + knownMapField.putAll( + mapOf(1 to Proto2MapEnum.PROTO2_MAP_ENUM_FOO, 2 to Proto2MapEnum.PROTO2_MAP_ENUM_BAR) + ) + knownMapField.remove(1) + assertThat(knownMapField).isEqualTo(mapOf(2 to Proto2MapEnum.PROTO2_MAP_ENUM_BAR)) + } + } + + @Test + fun testMapClear() { + val intMap = + testIntIntMap { + m.putAll(mapOf(1 to 2, 3 to 4)) + m.clear() + assertThat(m.isEmpty()).isTrue() + } + + testMaps { + mInt32.putAll(mapOf(1 to intMap, 2 to intMap)) + mInt32.clear() + assertThat(mInt32.isEmpty()).isTrue() + + mString.putAll(mapOf("1" to intMap, "2" to intMap)) + mString.clear() + assertThat(mString.isEmpty()).isTrue() + } + + testEnumMap { + knownMapField.putAll( + mapOf(1 to Proto2MapEnum.PROTO2_MAP_ENUM_FOO, 2 to Proto2MapEnum.PROTO2_MAP_ENUM_BAR) + ) + knownMapField.clear() + assertThat(knownMapField.isEmpty()).isTrue() + } + } + + @Test + fun testEvilNames() { + assertThat( + evilNamesProto2 { + initialized = true + hasFoo = true + bar = "foo" + isInitialized = true + fooBar = "foo" + aLLCAPS += "foo" + aLLCAPSMAP[1] = true + hasUnderbarPrecedingNumeric1Foo = true + hasUnderbarPrecedingNumeric42Bar = true + hasUnderbarPrecedingNumeric123Foo42BarBaz = true + extension += "foo" + class_ += 1 + int = 1.0 + long = true + boolean = 1L + sealed = "foo" + interface_ = 1F + in_ = 1 + object_ = "foo" + cachedSize_ = "foo" + serializedSize_ = true + by = "foo" + } + ).isEqualTo( + EvilNamesProto2.newBuilder() + .setInitialized(true) + .setHasFoo(true) + .setBar("foo") + .setIsInitialized(true) + .setFooBar("foo") + .addALLCAPS("foo") + .putALLCAPSMAP(1, true) + .setHasUnderbarPrecedingNumeric1Foo(true) + .setHasUnderbarPrecedingNumeric42Bar(true) + .setHasUnderbarPrecedingNumeric123Foo42BarBaz(true) + .addExtension("foo") + .addClass_(1) + .setInt(1.0) + .setLong(true) + .setBoolean(1L) + .setSealed("foo") + .setInterface(1F) + .setIn(1) + .setObject("foo") + .setCachedSize_("foo") + .setSerializedSize_(true) + .setBy("foo") + .build() + ) + + assertThat(interface_ {}).isEqualTo(Interface.newBuilder().build()) + } + + @Test + fun testHardKeywordGettersAndSetters() { + hardKeywordsAllTypes { + as_ = 1 + assertThat(as_).isEqualTo(1) + + in_ = "foo" + assertThat(in_).isEqualTo("foo") + + break_ = HardKeywordsAllTypes.NestedEnum.FOO + assertThat(break_).isEqualTo(HardKeywordsAllTypes.NestedEnum.FOO) + + do_ = HardKeywordsAllTypesKt.nestedMessage { while_ = 1 } + assertThat(do_).isEqualTo(HardKeywordsAllTypesKt.nestedMessage { while_ = 1 }) + + continue_[1] = 1 + assertThat(continue_[1]).isEqualTo(1) + + else_ += 1 + assertThat(else_).isEqualTo(listOf(1)) + + for_ += "foo" + assertThat(for_).isEqualTo(listOf("foo")) + + fun_ += HardKeywordsAllTypes.NestedEnum.FOO + assertThat(fun_).isEqualTo(listOf(HardKeywordsAllTypes.NestedEnum.FOO)) + + if_ += HardKeywordsAllTypesKt.nestedMessage { while_ = 1 } + assertThat(if_).isEqualTo(listOf(HardKeywordsAllTypesKt.nestedMessage { while_ = 1 })) + } + } + + @Test + fun testHardKeywordHazzers() { + hardKeywordsAllTypes { + as_ = 1 + assertThat(hasAs_()).isTrue() + + in_ = "foo" + assertThat(hasIn_()).isTrue() + + break_ = HardKeywordsAllTypes.NestedEnum.FOO + assertThat(hasBreak_()).isTrue() + + do_ = HardKeywordsAllTypesKt.nestedMessage { while_ = 1 } + assertThat(hasDo_()).isTrue() + } + } + + @Test + fun testHardKeywordClears() { + hardKeywordsAllTypes { + as_ = 1 + clearAs_() + assertThat(hasAs_()).isFalse() + + in_ = "foo" + clearIn_() + assertThat(hasIn_()).isFalse() + + break_ = HardKeywordsAllTypes.NestedEnum.FOO + clearBreak_() + assertThat(hasBreak_()).isFalse() + + do_ = HardKeywordsAllTypesKt.nestedMessage { while_ = 1 } + clearDo_() + assertThat(hasDo_()).isFalse() + } + } +} diff --git a/java/kotlin/src/test/kotlin/com/google/protobuf/Proto3Test.kt b/java/kotlin/src/test/kotlin/com/google/protobuf/Proto3Test.kt new file mode 100644 index 00000000000..b3fd3367316 --- /dev/null +++ b/java/kotlin/src/test/kotlin/com/google/protobuf/Proto3Test.kt @@ -0,0 +1,340 @@ +package com.google.protobuf.kotlin + +import com.google.common.truth.Truth.assertThat +import evil_names_proto3.EvilNamesProto3OuterClass.EvilNamesProto3 +import evil_names_proto3.EvilNamesProto3OuterClass.HardKeywordsAllTypes +import evil_names_proto3.EvilNamesProto3OuterClass.Class +import evil_names_proto3.HardKeywordsAllTypesKt +import evil_names_proto3.class_ +import evil_names_proto3.evilNamesProto3 +import evil_names_proto3.hardKeywordsAllTypes +import multiple_files_proto3.MultipleFilesMessageA +import multiple_files_proto3.MultipleFilesMessageB +import multiple_files_proto3.multipleFilesMessageA +import multiple_files_proto3.multipleFilesMessageB +import proto3_unittest.UnittestProto3 +import proto3_unittest.TestAllTypesKt +import proto3_unittest.TestAllTypesKt.nestedMessage +import proto3_unittest.UnittestProto3.TestAllTypes +import proto3_unittest.UnittestProto3.TestAllTypes.NestedEnum +import proto3_unittest.UnittestProto3.TestEmptyMessage +import proto3_unittest.copy +import proto3_unittest.testAllTypes +import proto3_unittest.testEmptyMessage +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@RunWith(JUnit4::class) +class Proto3Test { + @Test + fun testGettersAndSetters() { + testAllTypes { + optionalInt32 = 101 + assertThat(optionalInt32).isEqualTo(101) + optionalString = "115" + assertThat(optionalString).isEqualTo("115") + optionalNestedMessage = TestAllTypesKt.nestedMessage { bb = 118 } + assertThat(optionalNestedMessage).isEqualTo(TestAllTypesKt.nestedMessage { bb = 118 }) + optionalNestedEnum = NestedEnum.BAZ + assertThat(optionalNestedEnum).isEqualTo(NestedEnum.BAZ) + oneofUint32 = 601 + assertThat(oneofUint32).isEqualTo(601) + } + } + + @Test + fun testRepeatedGettersAndSetters() { + testAllTypes { + repeatedInt32.addAll(listOf(1, 2)) + assertThat(repeatedInt32).isEqualTo(listOf(1, 2)) + repeatedInt32 += listOf(3, 4) + assertThat(repeatedInt32).isEqualTo(listOf(1, 2, 3, 4)) + repeatedInt32[0] = 5 + assertThat(repeatedInt32).isEqualTo(listOf(5, 2, 3, 4)) + + repeatedString.addAll(listOf("1", "2")) + assertThat(repeatedString).isEqualTo(listOf("1", "2")) + repeatedString += listOf("3", "4") + assertThat(repeatedString).isEqualTo(listOf("1", "2", "3", "4")) + repeatedString[0] = "5" + assertThat(repeatedString).isEqualTo(listOf("5", "2", "3", "4")) + + repeatedNestedMessage.addAll(listOf(nestedMessage { bb = 1 }, nestedMessage { bb = 2 })) + assertThat(repeatedNestedMessage).isEqualTo( + listOf( + nestedMessage { bb = 1 }, + nestedMessage { bb = 2 } + ) + ) + repeatedNestedMessage += listOf(nestedMessage { bb = 3 }, nestedMessage { bb = 4 }) + assertThat(repeatedNestedMessage).isEqualTo( + listOf( + nestedMessage { bb = 1 }, + nestedMessage { bb = 2 }, + nestedMessage { bb = 3 }, + nestedMessage { bb = 4 } + ) + ) + repeatedNestedMessage[0] = nestedMessage { bb = 5 } + assertThat(repeatedNestedMessage).isEqualTo( + listOf( + nestedMessage { bb = 5 }, + nestedMessage { bb = 2 }, + nestedMessage { bb = 3 }, + nestedMessage { bb = 4 } + ) + ) + + repeatedNestedEnum.addAll(listOf(NestedEnum.FOO, NestedEnum.BAR)) + assertThat(repeatedNestedEnum).isEqualTo(listOf(NestedEnum.FOO, NestedEnum.BAR)) + repeatedNestedEnum += listOf(NestedEnum.BAZ, NestedEnum.FOO) + assertThat(repeatedNestedEnum).isEqualTo( + listOf(NestedEnum.FOO, NestedEnum.BAR, NestedEnum.BAZ, NestedEnum.FOO) + ) + repeatedNestedEnum[0] = NestedEnum.BAR + assertThat(repeatedNestedEnum).isEqualTo( + listOf(NestedEnum.BAR, NestedEnum.BAR, NestedEnum.BAZ, NestedEnum.FOO) + ) + } + } + + @Test + fun testClears() { + assertThat( + testAllTypes { + optionalInt32 = 101 + clearOptionalInt32() + + optionalString = "115" + clearOptionalString() + + optionalNestedMessage = TestAllTypesKt.nestedMessage { bb = 118 } + clearOptionalNestedMessage() + + optionalNestedEnum = NestedEnum.BAZ + clearOptionalNestedEnum() + + oneofUint32 = 601 + clearOneofUint32() + } + ).isEqualTo( + TestAllTypes.newBuilder().build() + ) + } + + @Test + fun testCopy() { + val message = testAllTypes { + optionalInt32 = 101 + optionalString = "115" + } + val modifiedMessage = message.copy { + optionalInt32 = 201 + } + + assertThat(message).isEqualTo( + TestAllTypes.newBuilder() + .setOptionalInt32(101) + .setOptionalString("115") + .build() + ) + assertThat(modifiedMessage).isEqualTo( + TestAllTypes.newBuilder() + .setOptionalInt32(201) + .setOptionalString("115") + .build() + ) + } + + @Test + fun testOneof() { + val message = testAllTypes { + oneofString = "foo" + assertThat(oneofFieldCase) + .isEqualTo(TestAllTypes.OneofFieldCase.ONEOF_STRING) + assertThat(oneofString).isEqualTo("foo") + clearOneofField() + assertThat(oneofFieldCase) + .isEqualTo(TestAllTypes.OneofFieldCase.ONEOFFIELD_NOT_SET) + oneofUint32 = 5 + } + + assertThat(message.getOneofFieldCase()) + .isEqualTo(TestAllTypes.OneofFieldCase.ONEOF_UINT32) + assertThat(message.getOneofUint32()).isEqualTo(5) + } + + @Test + fun testEmptyMessages() { + assertThat( + testEmptyMessage {} + ).isEqualTo( + TestEmptyMessage.newBuilder().build() + ) + } + + @Test + fun testEvilNames() { + assertThat( + evilNamesProto3 { + initialized = true + hasFoo = true + bar = "foo" + isInitialized = true + fooBar = "foo" + aLLCAPS += "foo" + aLLCAPSMAP[1] = true + hasUnderbarPrecedingNumeric1Foo = true + hasUnderbarPrecedingNumeric42Bar = true + hasUnderbarPrecedingNumeric123Foo42BarBaz = true + extension += "foo" + class_ = "foo" + int = 1.0 + long = true + boolean = 1L + sealed = "foo" + interface_ = 1F + in_ = 1 + object_ = "foo" + cachedSize_ = "foo" + serializedSize_ = true + value = "foo" + index = 1L + values += "foo" + newValues += "foo" + builder = true + k[1] = 1 + v["foo"] = "foo" + key["foo"] = 1 + map[1] = "foo" + pairs["foo"] = 1 + LeadingUnderscore = "foo" + option = 1 + } + ).isEqualTo( + EvilNamesProto3.newBuilder() + .setInitialized(true) + .setHasFoo(true) + .setBar("foo") + .setIsInitialized(true) + .setFooBar("foo") + .addALLCAPS("foo") + .putALLCAPSMAP(1, true) + .setHasUnderbarPrecedingNumeric1Foo(true) + .setHasUnderbarPrecedingNumeric42Bar(true) + .setHasUnderbarPrecedingNumeric123Foo42BarBaz(true) + .addExtension("foo") + .setClass_("foo") + .setInt(1.0) + .setLong(true) + .setBoolean(1L) + .setSealed("foo") + .setInterface(1F) + .setIn(1) + .setObject("foo") + .setCachedSize_("foo") + .setSerializedSize_(true) + .setValue("foo") + .setIndex(1L) + .addValues("foo") + .addNewValues("foo") + .setBuilder(true) + .putK(1, 1) + .putV("foo", "foo") + .putKey("foo", 1) + .putMap(1, "foo") + .putPairs("foo", 1) + .setLeadingUnderscore("foo") + .setOption(1) + .build() + ) + + assertThat(class_ {}).isEqualTo(Class.newBuilder().build()) + } + + @Test + fun testHardKeywordGettersAndSetters() { + hardKeywordsAllTypes { + as_ = 1 + assertThat(as_).isEqualTo(1) + + in_ = "foo" + assertThat(in_).isEqualTo("foo") + + break_ = HardKeywordsAllTypes.NestedEnum.FOO + assertThat(break_).isEqualTo(HardKeywordsAllTypes.NestedEnum.FOO) + + do_ = HardKeywordsAllTypesKt.nestedMessage { while_ = 1 } + assertThat(do_).isEqualTo(HardKeywordsAllTypesKt.nestedMessage { while_ = 1 }) + + continue_[1] = 1 + assertThat(continue_[1]).isEqualTo(1) + + else_ += 1 + assertThat(else_).isEqualTo(listOf(1)) + + for_ += "foo" + assertThat(for_).isEqualTo(listOf("foo")) + + fun_ += HardKeywordsAllTypes.NestedEnum.FOO + assertThat(fun_).isEqualTo(listOf(HardKeywordsAllTypes.NestedEnum.FOO)) + + if_ += HardKeywordsAllTypesKt.nestedMessage { while_ = 1 } + assertThat(if_).isEqualTo(listOf(HardKeywordsAllTypesKt.nestedMessage { while_ = 1 })) + } + } + + @Test + fun testHardKeywordHazzers() { + hardKeywordsAllTypes { + as_ = 1 + assertThat(hasAs_()).isTrue() + + in_ = "foo" + assertThat(hasIn_()).isTrue() + + break_ = HardKeywordsAllTypes.NestedEnum.FOO + assertThat(hasBreak_()).isTrue() + + do_ = HardKeywordsAllTypesKt.nestedMessage { while_ = 1 } + assertThat(hasDo_()).isTrue() + } + } + + @Test + fun testHardKeywordClears() { + hardKeywordsAllTypes { + as_ = 1 + clearAs_() + assertThat(hasAs_()).isFalse() + + in_ = "foo" + clearIn_() + assertThat(hasIn_()).isFalse() + + break_ = HardKeywordsAllTypes.NestedEnum.FOO + clearBreak_() + assertThat(hasBreak_()).isFalse() + + do_ = HardKeywordsAllTypesKt.nestedMessage { while_ = 1 } + clearDo_() + assertThat(hasDo_()).isFalse() + } + } + + @Test + fun testMultipleFiles() { + assertThat( + multipleFilesMessageA {} + ).isEqualTo( + MultipleFilesMessageA.newBuilder().build() + ) + + assertThat( + multipleFilesMessageB {} + ).isEqualTo( + MultipleFilesMessageB.newBuilder().build() + ) + } +} diff --git a/java/kotlin/src/test/proto/com/google/protobuf/evil_names_proto2.proto b/java/kotlin/src/test/proto/com/google/protobuf/evil_names_proto2.proto new file mode 100644 index 00000000000..70d813a1e72 --- /dev/null +++ b/java/kotlin/src/test/proto/com/google/protobuf/evil_names_proto2.proto @@ -0,0 +1,62 @@ +syntax = "proto2"; + +package evil_names_proto2; + +option java_package = "evil_names_proto2"; + +message EvilNamesProto2 { + optional bool initialized = 1; + optional bool has_foo = 2; + optional string Bar = 3; + optional bool is_initialized = 4; + + oneof camelCase { + string fooBar = 5; + } + + repeated string ALL_CAPS = 7; + map ALL_CAPS_MAP = 8; + + optional bool has_underbar_preceding_numeric_1foo = 9; + optional bool has_underbar_preceding_numeric_42bar = 13; + optional bool has_underbar_preceding_numeric_123foo42bar_baz = 14; + + extensions 100 to max; + + repeated string extension = 12; + repeated int32 class = 15; + optional double int = 16; + optional bool long = 17; + optional int64 boolean = 18; + optional string sealed = 19; + optional float interface = 20; + optional int32 in = 21; + optional string object = 22; + optional string cached_size = 23; + optional bool serialized_size = 24; + optional string by = 25; +} + +message HardKeywordsAllTypes { + message NestedMessage { + optional int32 while = 1; + } + + enum NestedEnum { + FOO = 1; + BAR = 2; + } + + optional int32 as = 1; + optional string in = 2; + optional NestedEnum break = 3; + map continue = 4; + optional NestedMessage do = 5; + + repeated int32 else = 6; + repeated string for = 7; + repeated NestedEnum fun = 8; + repeated NestedMessage if = 9; +} + +message Interface {} diff --git a/java/kotlin/src/test/proto/com/google/protobuf/evil_names_proto3.proto b/java/kotlin/src/test/proto/com/google/protobuf/evil_names_proto3.proto new file mode 100644 index 00000000000..1b40e4d8c1d --- /dev/null +++ b/java/kotlin/src/test/proto/com/google/protobuf/evil_names_proto3.proto @@ -0,0 +1,76 @@ +syntax = "proto3"; + +package evil_names_proto3; + +option java_package = "evil_names_proto3"; + +message EvilNamesProto3 { + bool initialized = 1; + bool has_foo = 2; + string Bar = 3; + bool is_initialized = 4; + + oneof camelCase { + string fooBar = 5; + } + + repeated string ALL_CAPS = 7; + map ALL_CAPS_MAP = 8; + + bool has_underbar_preceding_numeric_1foo = 9; + bool has_underbar_preceding_numeric_42bar = 10; + bool has_underbar_preceding_numeric_123foo42bar_baz = 11; + + repeated string extension = 12; + + string class = 13; + double int = 14; + bool long = 15; + int64 boolean = 16; + string sealed = 17; + float interface = 18; + int32 in = 19; + string object = 20; + string cached_size = 21; + bool serialized_size = 22; + string value = 23; + int64 index = 24; + repeated string values = 25; + repeated string new_values = 26; + bool builder = 27; + map k = 28; + map v = 29; + map key = 30; + map map = 31; + map pairs = 32; + + string _leading_underscore = 33; + oneof _leading_underscore_oneof { + int32 option = 34; + } +} + +message HardKeywordsAllTypes { + message NestedMessage { + optional int32 while = 1; + } + + enum NestedEnum { + ZERO = 0; + FOO = 1; + BAR = 2; + } + + optional int32 as = 1; + optional string in = 2; + optional NestedEnum break = 3; + map continue = 4; + optional NestedMessage do = 5; + + repeated int32 else = 6; + repeated string for = 7; + repeated NestedEnum fun = 8; + repeated NestedMessage if = 9; +} + +message Class {} diff --git a/java/kotlin/src/test/proto/com/google/protobuf/example_extensible_message.proto b/java/kotlin/src/test/proto/com/google/protobuf/example_extensible_message.proto new file mode 100644 index 00000000000..14f18dbdb1b --- /dev/null +++ b/java/kotlin/src/test/proto/com/google/protobuf/example_extensible_message.proto @@ -0,0 +1,16 @@ +syntax = "proto2"; + +package example_extensible_message; + +option java_package = "example_extensible_message"; +option java_multiple_files = true; + +message ExampleExtensibleMessage { + extensions 10 to 20; +} + +extend ExampleExtensibleMessage { + repeated int32 repeated_extension = 10; + repeated int32 different_extension = 11; + optional int32 int32_extension = 12; +} diff --git a/java/kotlin/src/test/proto/com/google/protobuf/multiple_files_proto3.proto b/java/kotlin/src/test/proto/com/google/protobuf/multiple_files_proto3.proto new file mode 100644 index 00000000000..82211ccd61e --- /dev/null +++ b/java/kotlin/src/test/proto/com/google/protobuf/multiple_files_proto3.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +package multiple_files_proto3; + +option java_package = "multiple_files_proto3"; +option java_multiple_files = true; + +enum NestedEnum { FOO = 0; } + +message MultipleFilesMessageA {} + +message MultipleFilesMessageB {} diff --git a/src/Makefile.am b/src/Makefile.am index 648c2679f30..4305e1b48e6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -150,6 +150,7 @@ nobase_include_HEADERS = \ google/protobuf/compiler/csharp/csharp_generator.h \ google/protobuf/compiler/csharp/csharp_names.h \ google/protobuf/compiler/java/java_generator.h \ + google/protobuf/compiler/java/java_kotlin_generator.h \ google/protobuf/compiler/java/java_names.h \ google/protobuf/compiler/js/js_generator.h \ google/protobuf/compiler/js/well_known_types_embed.h \ @@ -374,6 +375,7 @@ libprotoc_la_SOURCES = \ google/protobuf/compiler/java/java_generator_factory.h \ google/protobuf/compiler/java/java_helpers.cc \ google/protobuf/compiler/java/java_helpers.h \ + google/protobuf/compiler/java/java_kotlin_generator.cc \ google/protobuf/compiler/java/java_map_field.cc \ google/protobuf/compiler/java/java_map_field.h \ google/protobuf/compiler/java/java_map_field_lite.cc \ diff --git a/src/google/protobuf/compiler/java/java_enum_field.cc b/src/google/protobuf/compiler/java/java_enum_field.cc index 9a0799ee466..e68eb776e20 100644 --- a/src/google/protobuf/compiler/java/java_enum_field.cc +++ b/src/google/protobuf/compiler/java/java_enum_field.cc @@ -63,6 +63,7 @@ void SetEnumVariables(const FieldDescriptor* descriptor, int messageBitIndex, (*variables)["type"] = name_resolver->GetImmutableClassName(descriptor->enum_type()); + (*variables)["kt_type"] = (*variables)["type"]; (*variables)["mutable_type"] = name_resolver->GetMutableClassName(descriptor->enum_type()); (*variables)["default"] = ImmutableDefaultValue(descriptor, name_resolver); @@ -76,6 +77,11 @@ void SetEnumVariables(const FieldDescriptor* descriptor, int messageBitIndex, // by the proto compiler (*variables)["deprecation"] = descriptor->options().deprecated() ? "@java.lang.Deprecated " : ""; + (*variables)["kt_deprecation"] = + descriptor->options().deprecated() + ? "@kotlin.Deprecated(message = \"Field " + (*variables)["name"] + + " is deprecated\") " + : ""; (*variables)["on_changed"] = "onChanged();"; // Use deprecated valueOf() method to be compatible with old generated code // for v2.5.0/v2.6.1. @@ -270,6 +276,34 @@ void ImmutableEnumFieldGenerator::GenerateBuilderMembers( printer->Annotate("{", "}", descriptor_); } +void ImmutableEnumFieldGenerator::GenerateKotlinDslMembers( + io::Printer* printer) const { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$kt_deprecation$var $kt_name$: $kt_type$\n" + " @JvmName(\"${$get$kt_capitalized_name$$}$\")\n" + " get() = $kt_dsl_builder$.${$get$capitalized_name$$}$()\n" + " @JvmName(\"${$set$kt_capitalized_name$$}$\")\n" + " set(value) {\n" + " $kt_dsl_builder$.${$set$capitalized_name$$}$(value)\n" + " }\n"); + + WriteFieldAccessorDocComment(printer, descriptor_, CLEARER, + /* builder */ false); + printer->Print(variables_, + "fun ${$clear$kt_capitalized_name$$}$() {\n" + " $kt_dsl_builder$.${$clear$capitalized_name$$}$()\n" + "}\n"); + + if (HasHazzer(descriptor_)) { + WriteFieldAccessorDocComment(printer, descriptor_, HAZZER); + printer->Print(variables_, + "fun ${$has$kt_capitalized_name$$}$(): kotlin.Boolean {\n" + " return $kt_dsl_builder$.${$has$capitalized_name$$}$()\n" + "}\n"); + } +} + void ImmutableEnumFieldGenerator::GenerateFieldBuilderInitializationCode( io::Printer* printer) const { // noop for enums @@ -1037,6 +1071,98 @@ void RepeatedImmutableEnumFieldGenerator::GenerateHashCode( "}\n"); } +void RepeatedImmutableEnumFieldGenerator::GenerateKotlinDslMembers( + io::Printer* printer) const { + printer->Print( + variables_, + "/**\n" + " * An uninstantiable, behaviorless type to represent the field in\n" + " * generics.\n" + " */\n" + "@kotlin.OptIn" + "(com.google.protobuf.kotlin.OnlyForUseByGeneratedProtoCode::class)\n" + "class ${$$kt_capitalized_name$Proxy$}$ private constructor()" + " : com.google.protobuf.kotlin.DslProxy()\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$kt_deprecation$ val $kt_name$: " + "com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>\n" + " @kotlin.jvm.JvmSynthetic\n" + " get() = com.google.protobuf.kotlin.DslList(\n" + " $kt_dsl_builder$.${$get$capitalized_name$List$}$()\n" + " )\n"); + + WriteFieldAccessorDocComment(printer, descriptor_, LIST_ADDER, + /* builder */ false); + printer->Print(variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"add$kt_capitalized_name$\")\n" + "fun com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>." + "add(value: $kt_type$) {\n" + " $kt_dsl_builder$.${$add$capitalized_name$$}$(value)\n" + "}"); + + WriteFieldAccessorDocComment(printer, descriptor_, LIST_ADDER, + /* builder */ false); + printer->Print(variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"plusAssign$kt_capitalized_name$\")\n" + "inline operator fun com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>." + "plusAssign(value: $kt_type$) {\n" + " add(value)\n" + "}"); + + WriteFieldAccessorDocComment(printer, descriptor_, LIST_MULTI_ADDER, + /* builder */ false); + printer->Print(variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"addAll$kt_capitalized_name$\")\n" + "fun com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>." + "addAll(values: kotlin.collections.Iterable<$kt_type$>) {\n" + " $kt_dsl_builder$.${$addAll$capitalized_name$$}$(values)\n" + "}"); + + WriteFieldAccessorDocComment(printer, descriptor_, LIST_MULTI_ADDER, + /* builder */ false); + printer->Print( + variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"plusAssignAll$kt_capitalized_name$\")\n" + "inline operator fun com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>." + "plusAssign(values: kotlin.collections.Iterable<$kt_type$>) {\n" + " addAll(values)\n" + "}"); + + WriteFieldAccessorDocComment(printer, descriptor_, LIST_INDEXED_SETTER, + /* builder */ false); + printer->Print( + variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"set$kt_capitalized_name$\")\n" + "operator fun com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>." + "set(index: kotlin.Int, value: $kt_type$) {\n" + " $kt_dsl_builder$.${$set$capitalized_name$$}$(index, value)\n" + "}"); + + WriteFieldAccessorDocComment(printer, descriptor_, CLEARER, + /* builder */ false); + printer->Print(variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"clear$kt_capitalized_name$\")\n" + "fun com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>." + "clear() {\n" + " $kt_dsl_builder$.${$clear$capitalized_name$$}$()\n" + "}"); +} + std::string RepeatedImmutableEnumFieldGenerator::GetBoxedType() const { return name_resolver_->GetImmutableClassName(descriptor_->enum_type()); } diff --git a/src/google/protobuf/compiler/java/java_enum_field.h b/src/google/protobuf/compiler/java/java_enum_field.h index 95c7db578f2..a8cd033cf19 100644 --- a/src/google/protobuf/compiler/java/java_enum_field.h +++ b/src/google/protobuf/compiler/java/java_enum_field.h @@ -80,6 +80,7 @@ class ImmutableEnumFieldGenerator : public ImmutableFieldGenerator { void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; void GenerateEqualsCode(io::Printer* printer) const; void GenerateHashCode(io::Printer* printer) const; + void GenerateKotlinDslMembers(io::Printer* printer) const; std::string GetBoxedType() const; @@ -138,6 +139,7 @@ class RepeatedImmutableEnumFieldGenerator : public ImmutableFieldGenerator { void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; void GenerateEqualsCode(io::Printer* printer) const; void GenerateHashCode(io::Printer* printer) const; + void GenerateKotlinDslMembers(io::Printer* printer) const; std::string GetBoxedType() const; diff --git a/src/google/protobuf/compiler/java/java_enum_field_lite.cc b/src/google/protobuf/compiler/java/java_enum_field_lite.cc index 8403f95fbd6..dd442fec7aa 100644 --- a/src/google/protobuf/compiler/java/java_enum_field_lite.cc +++ b/src/google/protobuf/compiler/java/java_enum_field_lite.cc @@ -70,6 +70,7 @@ void SetEnumVariables(const FieldDescriptor* descriptor, int messageBitIndex, (*variables)["type"] = name_resolver->GetImmutableClassName(descriptor->enum_type()); + (*variables)["kt_type"] = (*variables)["type"]; (*variables)["mutable_type"] = name_resolver->GetMutableClassName(descriptor->enum_type()); (*variables)["default"] = ImmutableDefaultValue(descriptor, name_resolver); @@ -83,6 +84,11 @@ void SetEnumVariables(const FieldDescriptor* descriptor, int messageBitIndex, // by the proto compiler (*variables)["deprecation"] = descriptor->options().deprecated() ? "@java.lang.Deprecated " : ""; + (*variables)["kt_deprecation"] = + descriptor->options().deprecated() + ? "@kotlin.Deprecated(message = \"Field " + (*variables)["name"] + + " is deprecated\") " + : ""; (*variables)["required"] = descriptor->is_required() ? "true" : "false"; if (HasHasbit(descriptor)) { @@ -275,6 +281,34 @@ void ImmutableEnumFieldLiteGenerator::GenerateBuilderMembers( printer->Annotate("{", "}", descriptor_); } +void ImmutableEnumFieldLiteGenerator::GenerateKotlinDslMembers( + io::Printer* printer) const { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$kt_deprecation$var $kt_name$: $kt_type$\n" + " @JvmName(\"${$get$kt_capitalized_name$$}$\")\n" + " get() = $kt_dsl_builder$.${$get$capitalized_name$$}$()\n" + " @JvmName(\"${$set$kt_capitalized_name$$}$\")\n" + " set(value) {\n" + " $kt_dsl_builder$.${$set$capitalized_name$$}$(value)\n" + " }\n"); + + WriteFieldAccessorDocComment(printer, descriptor_, CLEARER, + /* builder */ false); + printer->Print(variables_, + "fun ${$clear$kt_capitalized_name$$}$() {\n" + " $kt_dsl_builder$.${$clear$capitalized_name$$}$()\n" + "}\n"); + + if (HasHazzer(descriptor_)) { + WriteFieldAccessorDocComment(printer, descriptor_, HAZZER); + printer->Print(variables_, + "fun ${$has$kt_capitalized_name$$}$(): kotlin.Boolean {\n" + " return $kt_dsl_builder$.${$has$capitalized_name$$}$()\n" + "}\n"); + } +} + void ImmutableEnumFieldLiteGenerator::GenerateInitializationCode( io::Printer* printer) const { if (!IsDefaultValueJavaDefault(descriptor_)) { @@ -774,6 +808,98 @@ void RepeatedImmutableEnumFieldLiteGenerator::GenerateInitializationCode( printer->Print(variables_, "$name$_ = emptyIntList();\n"); } +void RepeatedImmutableEnumFieldLiteGenerator::GenerateKotlinDslMembers( + io::Printer* printer) const { + printer->Print( + variables_, + "/**\n" + " * An uninstantiable, behaviorless type to represent the field in\n" + " * generics.\n" + " */\n" + "@kotlin.OptIn" + "(com.google.protobuf.kotlin.OnlyForUseByGeneratedProtoCode::class)\n" + "class ${$$kt_capitalized_name$Proxy$}$ private constructor()" + " : com.google.protobuf.kotlin.DslProxy()\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$kt_deprecation$ val $kt_name$: " + "com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>\n" + " @kotlin.jvm.JvmSynthetic\n" + " get() = com.google.protobuf.kotlin.DslList(\n" + " $kt_dsl_builder$.${$get$capitalized_name$List$}$()\n" + " )\n"); + + WriteFieldAccessorDocComment(printer, descriptor_, LIST_ADDER, + /* builder */ false); + printer->Print(variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"add$kt_capitalized_name$\")\n" + "fun com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>." + "add(value: $kt_type$) {\n" + " $kt_dsl_builder$.${$add$capitalized_name$$}$(value)\n" + "}"); + + WriteFieldAccessorDocComment(printer, descriptor_, LIST_ADDER, + /* builder */ false); + printer->Print(variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"plusAssign$kt_capitalized_name$\")\n" + "inline operator fun com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>." + "plusAssign(value: $kt_type$) {\n" + " add(value)\n" + "}"); + + WriteFieldAccessorDocComment(printer, descriptor_, LIST_MULTI_ADDER, + /* builder */ false); + printer->Print(variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"addAll$kt_capitalized_name$\")\n" + "fun com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>." + "addAll(values: kotlin.collections.Iterable<$kt_type$>) {\n" + " $kt_dsl_builder$.${$addAll$capitalized_name$$}$(values)\n" + "}"); + + WriteFieldAccessorDocComment(printer, descriptor_, LIST_MULTI_ADDER, + /* builder */ false); + printer->Print( + variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"plusAssignAll$kt_capitalized_name$\")\n" + "inline operator fun com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>." + "plusAssign(values: kotlin.collections.Iterable<$kt_type$>) {\n" + " addAll(values)\n" + "}"); + + WriteFieldAccessorDocComment(printer, descriptor_, LIST_INDEXED_SETTER, + /* builder */ false); + printer->Print( + variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"set$kt_capitalized_name$\")\n" + "operator fun com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>." + "set(index: kotlin.Int, value: $kt_type$) {\n" + " $kt_dsl_builder$.${$set$capitalized_name$$}$(index, value)\n" + "}"); + + WriteFieldAccessorDocComment(printer, descriptor_, CLEARER, + /* builder */ false); + printer->Print(variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"clear$kt_capitalized_name$\")\n" + "fun com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>." + "clear() {\n" + " $kt_dsl_builder$.${$clear$capitalized_name$$}$()\n" + "}"); +} + std::string RepeatedImmutableEnumFieldLiteGenerator::GetBoxedType() const { return name_resolver_->GetImmutableClassName(descriptor_->enum_type()); } diff --git a/src/google/protobuf/compiler/java/java_enum_field_lite.h b/src/google/protobuf/compiler/java/java_enum_field_lite.h index fbc11279b5f..99249fe632a 100644 --- a/src/google/protobuf/compiler/java/java_enum_field_lite.h +++ b/src/google/protobuf/compiler/java/java_enum_field_lite.h @@ -73,6 +73,7 @@ class ImmutableEnumFieldLiteGenerator : public ImmutableFieldLiteGenerator { void GenerateInitializationCode(io::Printer* printer) const; void GenerateFieldInfo(io::Printer* printer, std::vector* output) const; + void GenerateKotlinDslMembers(io::Printer* printer) const; std::string GetBoxedType() const; @@ -118,6 +119,7 @@ class RepeatedImmutableEnumFieldLiteGenerator void GenerateInitializationCode(io::Printer* printer) const; void GenerateFieldInfo(io::Printer* printer, std::vector* output) const; + void GenerateKotlinDslMembers(io::Printer* printer) const; std::string GetBoxedType() const; diff --git a/src/google/protobuf/compiler/java/java_field.cc b/src/google/protobuf/compiler/java/java_field.cc index 2f775a68a66..db5a332d8bc 100644 --- a/src/google/protobuf/compiler/java/java_field.cc +++ b/src/google/protobuf/compiler/java/java_field.cc @@ -258,6 +258,11 @@ void SetCommonFieldVariables(const FieldDescriptor* descriptor, // empty string. (*variables)["{"] = ""; (*variables)["}"] = ""; + (*variables)["kt_name"] = + IsForbiddenKotlin(info->name) ? info->name + "_" : info->name; + (*variables)["kt_capitalized_name"] = IsForbiddenKotlin(info->name) + ? info->capitalized_name + "_" + : info->capitalized_name; } void SetCommonOneofVariables(const FieldDescriptor* descriptor, diff --git a/src/google/protobuf/compiler/java/java_field.h b/src/google/protobuf/compiler/java/java_field.h index df6c38d7522..a7c995c4ef1 100644 --- a/src/google/protobuf/compiler/java/java_field.h +++ b/src/google/protobuf/compiler/java/java_field.h @@ -84,6 +84,7 @@ class ImmutableFieldGenerator { 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 GenerateEqualsCode(io::Printer* printer) const = 0; virtual void GenerateHashCode(io::Printer* printer) const = 0; @@ -106,6 +107,7 @@ 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; virtual std::string GetBoxedType() const = 0; diff --git a/src/google/protobuf/compiler/java/java_file.cc b/src/google/protobuf/compiler/java/java_file.cc index 5cdb00d5c13..e8801a10b9e 100644 --- a/src/google/protobuf/compiler/java/java_file.cc +++ b/src/google/protobuf/compiler/java/java_file.cc @@ -675,6 +675,53 @@ void FileGenerator::GenerateSiblings( } } +std::string FileGenerator::GetKotlinClassname() { + return name_resolver_->GetFileClassName(file_, immutable_api_, true); +} + +void FileGenerator::GenerateKotlinSiblings( + const std::string& package_dir, GeneratorContext* context, + std::vector* file_list, + std::vector* annotation_list) { + for (int i = 0; i < file_->message_type_count(); i++) { + const Descriptor* descriptor = file_->message_type(i); + MessageGenerator* generator = message_generators_[i].get(); + auto open_file = [context](const string& filename) { + return std::unique_ptr(context->Open(filename)); + }; + std::string filename = package_dir + descriptor->name() + "Kt.kt"; + file_list->push_back(filename); + std::string info_full_path = filename + ".pb.meta"; + GeneratedCodeInfo annotations; + io::AnnotationProtoCollector annotation_collector( + &annotations); + auto output = open_file(filename); + io::Printer printer( + output.get(), '$', + options_.annotate_code ? &annotation_collector : nullptr); + + printer.Print( + "//Generated by the protocol buffer compiler. DO NOT EDIT!\n" + "// source: $filename$\n" + "\n", + "filename", descriptor->file()->name()); + if (!java_package_.empty()) { + printer.Print( + "package $package$;\n" + "\n", + "package", java_package_); + } + + generator->GenerateKotlinMembers(&printer); + generator->GenerateTopLevelKotlinMembers(&printer); + + if (options_.annotate_code) { + auto info_output = open_file(info_full_path); + annotations.SerializeToZeroCopyStream(info_output.get()); + annotation_list->push_back(info_full_path); + } + } +} bool FileGenerator::ShouldIncludeDependency(const FileDescriptor* descriptor, bool immutable_api) { diff --git a/src/google/protobuf/compiler/java/java_file.h b/src/google/protobuf/compiler/java/java_file.h index 9f1f719237b..71ee3e85579 100644 --- a/src/google/protobuf/compiler/java/java_file.h +++ b/src/google/protobuf/compiler/java/java_file.h @@ -78,6 +78,11 @@ class FileGenerator { void Generate(io::Printer* printer); + std::string GetKotlinClassname(); + void GenerateKotlinSiblings(const std::string& package_dir, + GeneratorContext* generator_context, + std::vector* file_list, + std::vector* annotation_list); // If we aren't putting everything into one file, this will write all the // files other than the outer file (i.e. one for each message, enum, and diff --git a/src/google/protobuf/compiler/java/java_helpers.cc b/src/google/protobuf/compiler/java/java_helpers.cc index e32b6e32b52..10200437b4a 100644 --- a/src/google/protobuf/compiler/java/java_helpers.cc +++ b/src/google/protobuf/compiler/java/java_helpers.cc @@ -91,6 +91,17 @@ const std::unordered_set* kReservedNames = "transient", "try", "void", "volatile", "while", }); +// Names that should be avoided as field names in Kotlin. +// All Kotlin hard keywords are in this list. +const std::unordered_set* kKotlinForbiddenNames = + new std::unordered_set({ + "as", "as?", "break", "class", "continue", "do", "else", + "false", "for", "fun", "if", "in", "!in", "interface", + "is", "!is", "null", "object", "package", "return", "super", + "this", "throw", "true", "try", "typealias", "typeof", "val", + "var", "when", "while", + }); + bool IsForbidden(const std::string& field_name) { for (int i = 0; i < GOOGLE_ARRAYSIZE(kForbiddenWordList); ++i) { if (field_name == kForbiddenWordList[i]) { @@ -156,6 +167,38 @@ void PrintEnumVerifierLogic(io::Printer* printer, StrCat(enum_verifier_string, terminating_string).c_str()); } +std::string ToCamelCase(const std::string& input, bool lower_first) { + bool capitalize_next = !lower_first; + std::string result; + result.reserve(input.size()); + + for (char i : input) { + if (i == '_') { + capitalize_next = true; + } else if (capitalize_next) { + result.push_back(ToUpperCh(i)); + capitalize_next = false; + } else { + result.push_back(i); + } + } + + // Lower-case the first letter. + if (lower_first && !result.empty()) { + result[0] = ToLowerCh(result[0]); + } + + return result; +} + +char ToUpperCh(char ch) { + return (ch >= 'a' && ch <= 'z') ? (ch - 'a' + 'A') : ch; +} + +char ToLowerCh(char ch) { + return (ch >= 'A' && ch <= 'Z') ? (ch - 'A' + 'a') : ch; +} + std::string UnderscoresToCamelCase(const std::string& input, bool cap_next_letter) { GOOGLE_CHECK(!input.empty()); @@ -217,6 +260,10 @@ std::string UnderscoresToCamelCaseCheckReserved(const FieldDescriptor* field) { return name; } +bool IsForbiddenKotlin(const std::string& field_name) { + return kKotlinForbiddenNames->find(field_name) != + kKotlinForbiddenNames->end(); +} std::string UniqueFileScopeIdentifier(const Descriptor* descriptor) { return "static_" + StringReplace(descriptor->full_name(), ".", "_", true); @@ -423,6 +470,35 @@ const char* BoxedPrimitiveTypeName(const FieldDescriptor* descriptor) { return BoxedPrimitiveTypeName(GetJavaType(descriptor)); } +const char* KotlinTypeName(JavaType type) { + switch (type) { + case JAVATYPE_INT: + return "kotlin.Int"; + case JAVATYPE_LONG: + return "kotlin.Long"; + case JAVATYPE_FLOAT: + return "kotlin.Float"; + case JAVATYPE_DOUBLE: + return "kotlin.Double"; + case JAVATYPE_BOOLEAN: + return "kotlin.Boolean"; + case JAVATYPE_STRING: + return "kotlin.String"; + case JAVATYPE_BYTES: + return "com.google.protobuf.ByteString"; + case JAVATYPE_ENUM: + return NULL; + case JAVATYPE_MESSAGE: + return NULL; + + // No default because we want the compiler to complain if any new + // JavaTypes are added. + } + + GOOGLE_LOG(FATAL) << "Can't get here."; + return NULL; +} + std::string GetOneofStoredType(const FieldDescriptor* field) { const JavaType javaType = GetJavaType(field); diff --git a/src/google/protobuf/compiler/java/java_helpers.h b/src/google/protobuf/compiler/java/java_helpers.h index 5ede13cc770..13dd9035671 100644 --- a/src/google/protobuf/compiler/java/java_helpers.h +++ b/src/google/protobuf/compiler/java/java_helpers.h @@ -53,6 +53,7 @@ namespace java { extern const char kThickSeparator[]; extern const char kThinSeparator[]; +bool IsForbiddenKotlin(const std::string& field_name); // If annotation_file is non-empty, prints a javax.annotation.Generated // annotation to the given Printer. annotation_file will be referenced in the @@ -73,6 +74,13 @@ void PrintEnumVerifierLogic(io::Printer* printer, const char* var_name, const char* terminating_string, bool enforce_lite); +// Converts a name to camel-case. If cap_first_letter is true, capitalize the +// first letter. +std::string ToCamelCase(const std::string& input, bool lower_first); + +char ToUpperCh(char ch); +char ToLowerCh(char ch); + // Converts a name to camel-case. If cap_first_letter is true, capitalize the // first letter. std::string UnderscoresToCamelCase(const std::string& name, @@ -216,6 +224,9 @@ const char* PrimitiveTypeName(JavaType type); // types. const char* BoxedPrimitiveTypeName(JavaType type); +// Kotlin source does not distinguish between primitives and non-primitives, +// but does use Kotlin-specific qualified types for them. +const char* KotlinTypeName(JavaType type); // Get the name of the java enum constant representing this type. E.g., // "INT32" for FieldDescriptor::TYPE_INT32. The enum constant's full diff --git a/src/google/protobuf/compiler/java/java_kotlin_generator.cc b/src/google/protobuf/compiler/java/java_kotlin_generator.cc new file mode 100644 index 00000000000..5a5d198ce53 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_kotlin_generator.cc @@ -0,0 +1,130 @@ +#include + +#include + +#include +#include +#include + +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +KotlinGenerator::KotlinGenerator() {} +KotlinGenerator::~KotlinGenerator() {} + +uint64_t KotlinGenerator::GetSupportedFeatures() const { + return CodeGenerator::Feature::FEATURE_PROTO3_OPTIONAL; +} + +bool KotlinGenerator::Generate(const FileDescriptor* file, + const std::string& parameter, + GeneratorContext* context, + std::string* error) const { + // ----------------------------------------------------------------- + // parse generator options + + std::vector > options; + ParseGeneratorParameter(parameter, &options); + Options file_options; + + for (auto& option : options) { + if (option.first == "output_list_file") { + file_options.output_list_file = option.second; + } else if (option.first == "immutable") { + file_options.generate_immutable_code = true; + } else if (option.first == "mutable") { + *error = "Mutable not supported by Kotlin generator"; + return false; + } else if (option.first == "shared") { + file_options.generate_shared_code = true; + } else if (option.first == "lite") { + file_options.enforce_lite = true; + } else if (option.first == "annotate_code") { + file_options.annotate_code = true; + } else if (option.first == "annotation_list_file") { + file_options.annotation_list_file = option.second; + } else { + *error = "Unknown generator option: " + option.first; + return false; + } + } + + // By default we generate immutable code and shared code for immutable API. + if (!file_options.generate_immutable_code && + !file_options.generate_shared_code) { + file_options.generate_immutable_code = true; + file_options.generate_shared_code = true; + } + + std::vector all_files; + std::vector all_annotations; + + std::unique_ptr file_generator(new FileGenerator(file, file_options, /* immutable_api = */ true)); + + if (!file_generator->Validate(error)) { + return false; + } + + auto open_file = [context](const string& filename) { + return std::unique_ptr(context->Open(filename)); + }; + std::string package_dir = JavaPackageToDir(file_generator->java_package()); + std::string kotlin_filename = package_dir; + kotlin_filename += file_generator->GetKotlinClassname(); + kotlin_filename += ".kt"; + all_files.push_back(kotlin_filename); + std::string info_full_path = kotlin_filename + ".pb.meta"; + if (file_options.annotate_code) { + all_annotations.push_back(info_full_path); + } + + // Generate main kotlin file. + auto output = open_file(kotlin_filename); + GeneratedCodeInfo annotations; + io::AnnotationProtoCollector annotation_collector( + &annotations); + io::Printer printer( + output.get(), '$', + file_options.annotate_code ? &annotation_collector : nullptr); + + file_generator->GenerateKotlinSiblings(package_dir, context, &all_files, + &all_annotations); + + if (file_options.annotate_code) { + auto info_output = open_file(info_full_path); + annotations.SerializeToZeroCopyStream(info_output.get()); + } + + // Generate output list if requested. + if (!file_options.output_list_file.empty()) { + // Generate output list. This is just a simple text file placed in a + // deterministic location which lists the .kt files being generated. + auto srclist_raw_output = open_file(file_options.output_list_file); + io::Printer srclist_printer(srclist_raw_output.get(), '$'); + for (auto& all_file : all_files) { + srclist_printer.Print("$filename$\n", "filename", all_file); + } + } + + if (!file_options.annotation_list_file.empty()) { + // Generate output list. This is just a simple text file placed in a + // deterministic location which lists the .kt files being generated. + auto annotation_list_raw_output = + open_file(file_options.annotation_list_file); + io::Printer annotation_list_printer(annotation_list_raw_output.get(), '$'); + for (auto& all_annotation : all_annotations) { + annotation_list_printer.Print("$filename$\n", "filename", all_annotation); + } + } + + return true; +} + +} // namespace java +} // namespace compiler +} //namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_kotlin_generator.h b/src/google/protobuf/compiler/java/java_kotlin_generator.h new file mode 100644 index 00000000000..db15353c0ad --- /dev/null +++ b/src/google/protobuf/compiler/java/java_kotlin_generator.h @@ -0,0 +1,40 @@ +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_KOTLIN_GENERATOR_H_ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_KOTLIN_GENERATOR_H_ + +#include +#include + +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +// CodeGenerator implementation which generates Kotlin code. If you create your +// own protocol compiler binary and you want it to support Kotlin output, you +// can do so by registering an instance of this CodeGenerator with the +// CommandLineInterface in your main() function. +class PROTOC_EXPORT KotlinGenerator : public CodeGenerator { + public: + KotlinGenerator(); + ~KotlinGenerator() override; + + // implements CodeGenerator ---------------------------------------- + bool Generate(const FileDescriptor* file, const std::string& parameter, + GeneratorContext* context, std::string* error) const override; + + uint64_t GetSupportedFeatures() const override; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(KotlinGenerator); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google + +#include + +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_KOTLIN_GENERATOR_H_ diff --git a/src/google/protobuf/compiler/java/java_map_field.cc b/src/google/protobuf/compiler/java/java_map_field.cc index 5db199d38fa..a4ec2ffad78 100644 --- a/src/google/protobuf/compiler/java/java_map_field.cc +++ b/src/google/protobuf/compiler/java/java_map_field.cc @@ -69,6 +69,17 @@ std::string TypeName(const FieldDescriptor* field, } } +std::string KotlinTypeName(const FieldDescriptor* field, + ClassNameResolver* name_resolver) { + if (GetJavaType(field) == JAVATYPE_MESSAGE) { + return name_resolver->GetImmutableClassName(field->message_type()); + } else if (GetJavaType(field) == JAVATYPE_ENUM) { + return name_resolver->GetImmutableClassName(field->enum_type()); + } else { + return KotlinTypeName(GetJavaType(field)); + } +} + std::string WireType(const FieldDescriptor* field) { return "com.google.protobuf.WireFormat.FieldType." + std::string(FieldTypeName(field->type())); @@ -91,6 +102,8 @@ void SetMessageVariables(const FieldDescriptor* descriptor, int messageBitIndex, (*variables)["key_type"] = TypeName(key, name_resolver, false); std::string boxed_key_type = TypeName(key, name_resolver, true); (*variables)["boxed_key_type"] = boxed_key_type; + (*variables)["kt_key_type"] = KotlinTypeName(key, name_resolver); + (*variables)["kt_value_type"] = KotlinTypeName(value, name_resolver); // Used for calling the serialization function. (*variables)["short_key_type"] = boxed_key_type.substr(boxed_key_type.rfind('.') + 1); @@ -136,6 +149,11 @@ void SetMessageVariables(const FieldDescriptor* descriptor, int messageBitIndex, // by the proto compiler (*variables)["deprecation"] = descriptor->options().deprecated() ? "@java.lang.Deprecated " : ""; + (*variables)["kt_deprecation"] = + descriptor->options().deprecated() + ? "@kotlin.Deprecated(message = \"Field " + (*variables)["name"] + + " is deprecated\") " + : ""; (*variables)["on_changed"] = "onChanged();"; // For repeated fields, one bit is used for whether the array is immutable @@ -651,6 +669,87 @@ void ImmutableMapFieldGenerator::GenerateMapGetters( } } +void ImmutableMapFieldGenerator::GenerateKotlinDslMembers( + io::Printer* printer) const { + printer->Print( + variables_, + "/**\n" + " * An uninstantiable, behaviorless type to represent the field in\n" + " * generics.\n" + " */\n" + "@kotlin.OptIn" + "(com.google.protobuf.kotlin.OnlyForUseByGeneratedProtoCode::class)\n" + "class ${$$kt_capitalized_name$Proxy$}$ private constructor()" + " : com.google.protobuf.kotlin.DslProxy()\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "$kt_deprecation$ val $kt_name$: " + "com.google.protobuf.kotlin.DslMap" + "<$kt_key_type$, $kt_value_type$, ${$$kt_capitalized_name$Proxy$}$>\n" + " @kotlin.jvm.JvmSynthetic\n" + " @JvmName(\"get$kt_capitalized_name$Map\")\n" + " get() = com.google.protobuf.kotlin.DslMap(\n" + " $kt_dsl_builder$.${$get$capitalized_name$Map$}$()\n" + " )\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "@JvmName(\"put$kt_capitalized_name$\")\n" + "fun com.google.protobuf.kotlin.DslMap" + "<$kt_key_type$, $kt_value_type$, ${$$kt_capitalized_name$Proxy$}$>\n" + " .put(key: $kt_key_type$, value: $kt_value_type$) {\n" + " $kt_dsl_builder$.${$put$capitalized_name$$}$(key, value)\n" + " }\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@JvmName(\"set$kt_capitalized_name$\")\n" + "inline operator fun com.google.protobuf.kotlin.DslMap" + "<$kt_key_type$, $kt_value_type$, ${$$kt_capitalized_name$Proxy$}$>\n" + " .set(key: $kt_key_type$, value: $kt_value_type$) {\n" + " put(key, value)\n" + " }\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@JvmName(\"remove$kt_capitalized_name$\")\n" + "fun com.google.protobuf.kotlin.DslMap" + "<$kt_key_type$, $kt_value_type$, ${$$kt_capitalized_name$Proxy$}$>\n" + " .remove(key: $kt_key_type$) {\n" + " $kt_dsl_builder$.${$remove$capitalized_name$$}$(key)\n" + " }\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@JvmName(\"putAll$kt_capitalized_name$\")\n" + "fun com.google.protobuf.kotlin.DslMap" + "<$kt_key_type$, $kt_value_type$, ${$$kt_capitalized_name$Proxy$}$>\n" + " .putAll(map: kotlin.collections.Map<$kt_key_type$, $kt_value_type$>) " + "{\n" + " $kt_dsl_builder$.${$putAll$capitalized_name$$}$(map)\n" + " }\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@JvmName(\"clear$kt_capitalized_name$\")\n" + "fun com.google.protobuf.kotlin.DslMap" + "<$kt_key_type$, $kt_value_type$, ${$$kt_capitalized_name$Proxy$}$>\n" + " .clear() {\n" + " $kt_dsl_builder$.${$clear$capitalized_name$$}$()\n" + " }\n"); +} + void ImmutableMapFieldGenerator::GenerateFieldBuilderInitializationCode( io::Printer* printer) const { // Nothing to initialize. diff --git a/src/google/protobuf/compiler/java/java_map_field.h b/src/google/protobuf/compiler/java/java_map_field.h index 2ff1f7673e9..63b25770039 100644 --- a/src/google/protobuf/compiler/java/java_map_field.h +++ b/src/google/protobuf/compiler/java/java_map_field.h @@ -62,6 +62,7 @@ class ImmutableMapFieldGenerator : public ImmutableFieldGenerator { void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; void GenerateEqualsCode(io::Printer* printer) const; void GenerateHashCode(io::Printer* printer) const; + void GenerateKotlinDslMembers(io::Printer* printer) const; std::string GetBoxedType() const; diff --git a/src/google/protobuf/compiler/java/java_map_field_lite.cc b/src/google/protobuf/compiler/java/java_map_field_lite.cc index 8db281d93eb..023b4fcccb0 100644 --- a/src/google/protobuf/compiler/java/java_map_field_lite.cc +++ b/src/google/protobuf/compiler/java/java_map_field_lite.cc @@ -71,6 +71,17 @@ std::string TypeName(const FieldDescriptor* field, } } +std::string KotlinTypeName(const FieldDescriptor* field, + ClassNameResolver* name_resolver) { + if (GetJavaType(field) == JAVATYPE_MESSAGE) { + return name_resolver->GetImmutableClassName(field->message_type()); + } else if (GetJavaType(field) == JAVATYPE_ENUM) { + return name_resolver->GetImmutableClassName(field->enum_type()); + } else { + return KotlinTypeName(GetJavaType(field)); + } +} + std::string WireType(const FieldDescriptor* field) { return "com.google.protobuf.WireFormat.FieldType." + std::string(FieldTypeName(field->type())); @@ -92,6 +103,8 @@ void SetMessageVariables(const FieldDescriptor* descriptor, int messageBitIndex, (*variables)["key_type"] = TypeName(key, name_resolver, false); (*variables)["boxed_key_type"] = TypeName(key, name_resolver, true); + (*variables)["kt_key_type"] = KotlinTypeName(key, name_resolver); + (*variables)["kt_value_type"] = KotlinTypeName(value, name_resolver); (*variables)["key_wire_type"] = WireType(key); (*variables)["key_default_value"] = DefaultValue(key, true, name_resolver); // We use `x.getClass()` as a null check because it generates less bytecode @@ -137,6 +150,11 @@ void SetMessageVariables(const FieldDescriptor* descriptor, int messageBitIndex, // by the proto compiler (*variables)["deprecation"] = descriptor->options().deprecated() ? "@java.lang.Deprecated " : ""; + (*variables)["kt_deprecation"] = + descriptor->options().deprecated() + ? "@kotlin.Deprecated(message = \"Field " + (*variables)["name"] + + " is deprecated\") " + : ""; (*variables)["default_entry"] = (*variables)["capitalized_name"] + "DefaultEntryHolder.defaultEntry"; @@ -794,6 +812,87 @@ void ImmutableMapFieldLiteGenerator::GenerateBuilderMembers( } } +void ImmutableMapFieldLiteGenerator::GenerateKotlinDslMembers( + io::Printer* printer) const { + printer->Print( + variables_, + "/**\n" + " * An uninstantiable, behaviorless type to represent the field in\n" + " * generics.\n" + " */\n" + "@kotlin.OptIn" + "(com.google.protobuf.kotlin.OnlyForUseByGeneratedProtoCode::class)\n" + "class ${$$kt_capitalized_name$Proxy$}$ private constructor()" + " : com.google.protobuf.kotlin.DslProxy()\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "$kt_deprecation$ val $kt_name$: " + "com.google.protobuf.kotlin.DslMap" + "<$kt_key_type$, $kt_value_type$, ${$$kt_capitalized_name$Proxy$}$>\n" + " @kotlin.jvm.JvmSynthetic\n" + " @JvmName(\"get$kt_capitalized_name$Map\")\n" + " get() = com.google.protobuf.kotlin.DslMap(\n" + " $kt_dsl_builder$.${$get$capitalized_name$Map$}$()\n" + " )\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "@JvmName(\"put$kt_capitalized_name$\")\n" + "fun com.google.protobuf.kotlin.DslMap" + "<$kt_key_type$, $kt_value_type$, ${$$kt_capitalized_name$Proxy$}$>\n" + " .put(key: $kt_key_type$, value: $kt_value_type$) {\n" + " $kt_dsl_builder$.${$put$capitalized_name$$}$(key, value)\n" + " }\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@JvmName(\"set$kt_capitalized_name$\")\n" + "inline operator fun com.google.protobuf.kotlin.DslMap" + "<$kt_key_type$, $kt_value_type$, ${$$kt_capitalized_name$Proxy$}$>\n" + " .set(key: $kt_key_type$, value: $kt_value_type$) {\n" + " put(key, value)\n" + " }\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@JvmName(\"remove$kt_capitalized_name$\")\n" + "fun com.google.protobuf.kotlin.DslMap" + "<$kt_key_type$, $kt_value_type$, ${$$kt_capitalized_name$Proxy$}$>\n" + " .remove(key: $kt_key_type$) {\n" + " $kt_dsl_builder$.${$remove$capitalized_name$$}$(key)\n" + " }\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@JvmName(\"putAll$kt_capitalized_name$\")\n" + "fun com.google.protobuf.kotlin.DslMap" + "<$kt_key_type$, $kt_value_type$, ${$$kt_capitalized_name$Proxy$}$>\n" + " .putAll(map: kotlin.collections.Map<$kt_key_type$, $kt_value_type$>) " + "{\n" + " $kt_dsl_builder$.${$putAll$capitalized_name$$}$(map)\n" + " }\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print( + variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@JvmName(\"clear$kt_capitalized_name$\")\n" + "fun com.google.protobuf.kotlin.DslMap" + "<$kt_key_type$, $kt_value_type$, ${$$kt_capitalized_name$Proxy$}$>\n" + " .clear() {\n" + " $kt_dsl_builder$.${$clear$capitalized_name$$}$()\n" + " }\n"); +} + void ImmutableMapFieldLiteGenerator::GenerateInitializationCode( io::Printer* printer) const { // Nothing to initialize. diff --git a/src/google/protobuf/compiler/java/java_map_field_lite.h b/src/google/protobuf/compiler/java/java_map_field_lite.h index 37aec063983..62c162d6b2f 100644 --- a/src/google/protobuf/compiler/java/java_map_field_lite.h +++ b/src/google/protobuf/compiler/java/java_map_field_lite.h @@ -55,6 +55,7 @@ class ImmutableMapFieldLiteGenerator : public ImmutableFieldLiteGenerator { void GenerateInitializationCode(io::Printer* printer) const; void GenerateFieldInfo(io::Printer* printer, std::vector* output) const; + void GenerateKotlinDslMembers(io::Printer* printer) const; std::string GetBoxedType() const; diff --git a/src/google/protobuf/compiler/java/java_message.cc b/src/google/protobuf/compiler/java/java_message.cc index f2df25f0feb..5425ad9accb 100644 --- a/src/google/protobuf/compiler/java/java_message.cc +++ b/src/google/protobuf/compiler/java/java_message.cc @@ -1358,6 +1358,243 @@ void ImmutableMessageGenerator::GenerateInitializers(io::Printer* printer) { } } +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( + "class Dsl private constructor(\n" + " @kotlin.jvm.JvmField private val _builder: $message$.Builder\n" + ") {\n" + " 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", 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 oneof : oneofs_) { + printer->Print( + "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" + "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", + 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.JvmSynthetic\n" + "inline fun $camelcase_name$(block: $message_kt$.Dsl.() -> Unit): " + "$message$ " + "=\n" + " $message_kt$.Dsl._create($message$.newBuilder()).apply { block() " + "}._build()\n", + "camelcase_name", name_resolver_->GetKotlinFactoryName(descriptor_), + "message_kt", name_resolver_->GetKotlinExtensionsClassName(descriptor_), + "message", name_resolver_->GetClassName(descriptor_, true)); + + printer->Print("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; + ImmutableMessageGenerator(descriptor_->nested_type(i), context_) + .GenerateKotlinMembers(printer); + } + printer->Outdent(); + printer->Print("}\n"); +} + +void ImmutableMessageGenerator::GenerateTopLevelKotlinMembers( + io::Printer* printer) const { + printer->Print( + "@kotlin.jvm.JvmSynthetic\n" + "inline fun $message$.copy(block: $message_kt$.Dsl.() -> Unit): " + "$message$ =\n" + " $message_kt$.Dsl._create(this.toBuilder()).apply { block() " + "}._build()\n", + "message", name_resolver_->GetClassName(descriptor_, true), "message_kt", + name_resolver_->GetKotlinExtensionsClassName(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); + } +} + +void ImmutableMessageGenerator::GenerateKotlinExtensions( + io::Printer* printer) const { + std::string message_name = name_resolver_->GetClassName(descriptor_, true); + + printer->Print( + "@Suppress(\"UNCHECKED_CAST\")\n" + "@kotlin.jvm.JvmSynthetic\n" + "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$, " + "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" + "operator fun get(\n" + " extension: com.google.protobuf.ExtensionLite<$message$, 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" + "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" + "fun clear(extension: com.google.protobuf.ExtensionLite<$message$, *>) " + "{\n" + " _builder.clearExtension(extension)\n" + "}\n\n", + "message", message_name); + + printer->Print( + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.PublishedApi\n" + "internal 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" + "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" + "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" + "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" + "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" + "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" + "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" + "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" + "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" + "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/java_message.h b/src/google/protobuf/compiler/java/java_message.h index 87b9df5e598..50d718039af 100644 --- a/src/google/protobuf/compiler/java/java_message.h +++ b/src/google/protobuf/compiler/java/java_message.h @@ -85,6 +85,9 @@ 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_; @@ -107,6 +110,9 @@ class ImmutableMessageGenerator : public MessageGenerator { // Returns an estimate of the number of bytes the printed code will compile to virtual int GenerateStaticVariableInitializers(io::Printer* printer); + 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); @@ -128,6 +134,7 @@ class ImmutableMessageGenerator : public MessageGenerator { void GenerateEqualsAndHashCode(io::Printer* printer); void GenerateParser(io::Printer* printer); void GenerateParsingConstructor(io::Printer* printer); + void GenerateKotlinExtensions(io::Printer* printer) const; void GenerateAnyMethods(io::Printer* printer); Context* context_; diff --git a/src/google/protobuf/compiler/java/java_message_field.cc b/src/google/protobuf/compiler/java/java_message_field.cc index f657c179574..a6d5dfe60b7 100644 --- a/src/google/protobuf/compiler/java/java_message_field.cc +++ b/src/google/protobuf/compiler/java/java_message_field.cc @@ -60,6 +60,7 @@ void SetMessageVariables(const FieldDescriptor* descriptor, int messageBitIndex, (*variables)["type"] = name_resolver->GetImmutableClassName(descriptor->message_type()); + (*variables)["kt_type"] = (*variables)["type"]; (*variables)["mutable_type"] = name_resolver->GetMutableClassName(descriptor->message_type()); (*variables)["group_or_message"] = @@ -69,6 +70,11 @@ void SetMessageVariables(const FieldDescriptor* descriptor, int messageBitIndex, // by the proto compiler (*variables)["deprecation"] = descriptor->options().deprecated() ? "@java.lang.Deprecated " : ""; + (*variables)["kt_deprecation"] = + descriptor->options().deprecated() + ? "@kotlin.Deprecated(message = \"Field " + (*variables)["name"] + + " is deprecated\") " + : ""; (*variables)["on_changed"] = "onChanged();"; (*variables)["ver"] = GeneratedCodeVersionSuffix(); (*variables)["get_parser"] = @@ -407,6 +413,32 @@ void ImmutableMessageFieldGenerator::GenerateBuilderMembers( "}\n"); } +void ImmutableMessageFieldGenerator::GenerateKotlinDslMembers( + io::Printer* printer) const { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$kt_deprecation$var $kt_name$: $kt_type$\n" + " @JvmName(\"${$get$kt_capitalized_name$$}$\")\n" + " get() = $kt_dsl_builder$.${$get$capitalized_name$$}$()\n" + " @JvmName(\"${$set$kt_capitalized_name$$}$\")\n" + " set(value) {\n" + " $kt_dsl_builder$.${$set$capitalized_name$$}$(value)\n" + " }\n"); + + WriteFieldAccessorDocComment(printer, descriptor_, CLEARER, + /* builder */ false); + printer->Print(variables_, + "fun ${$clear$kt_capitalized_name$$}$() {\n" + " $kt_dsl_builder$.${$clear$capitalized_name$$}$()\n" + "}\n"); + + WriteFieldAccessorDocComment(printer, descriptor_, HAZZER); + printer->Print(variables_, + "fun ${$has$kt_capitalized_name$$}$(): kotlin.Boolean {\n" + " return $kt_dsl_builder$.${$has$capitalized_name$$}$()\n" + "}\n"); +} + void ImmutableMessageFieldGenerator::GenerateFieldBuilderInitializationCode( io::Printer* printer) const { if (HasHasbit(descriptor_)) { @@ -1361,6 +1393,98 @@ std::string RepeatedImmutableMessageFieldGenerator::GetBoxedType() const { return name_resolver_->GetImmutableClassName(descriptor_->message_type()); } +void RepeatedImmutableMessageFieldGenerator::GenerateKotlinDslMembers( + io::Printer* printer) const { + printer->Print( + variables_, + "/**\n" + " * An uninstantiable, behaviorless type to represent the field in\n" + " * generics.\n" + " */\n" + "@kotlin.OptIn" + "(com.google.protobuf.kotlin.OnlyForUseByGeneratedProtoCode::class)\n" + "class ${$$kt_capitalized_name$Proxy$}$ private constructor()" + " : com.google.protobuf.kotlin.DslProxy()\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$kt_deprecation$ val $kt_name$: " + "com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>\n" + " @kotlin.jvm.JvmSynthetic\n" + " get() = com.google.protobuf.kotlin.DslList(\n" + " $kt_dsl_builder$.${$get$capitalized_name$List$}$()\n" + " )\n"); + + WriteFieldAccessorDocComment(printer, descriptor_, LIST_ADDER, + /* builder */ false); + printer->Print(variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"add$kt_capitalized_name$\")\n" + "fun com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>." + "add(value: $kt_type$) {\n" + " $kt_dsl_builder$.${$add$capitalized_name$$}$(value)\n" + "}"); + + WriteFieldAccessorDocComment(printer, descriptor_, LIST_ADDER, + /* builder */ false); + printer->Print(variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"plusAssign$kt_capitalized_name$\")\n" + "inline operator fun com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>." + "plusAssign(value: $kt_type$) {\n" + " add(value)\n" + "}"); + + WriteFieldAccessorDocComment(printer, descriptor_, LIST_MULTI_ADDER, + /* builder */ false); + printer->Print(variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"addAll$kt_capitalized_name$\")\n" + "fun com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>." + "addAll(values: kotlin.collections.Iterable<$kt_type$>) {\n" + " $kt_dsl_builder$.${$addAll$capitalized_name$$}$(values)\n" + "}"); + + WriteFieldAccessorDocComment(printer, descriptor_, LIST_MULTI_ADDER, + /* builder */ false); + printer->Print( + variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"plusAssignAll$kt_capitalized_name$\")\n" + "inline operator fun com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>." + "plusAssign(values: kotlin.collections.Iterable<$kt_type$>) {\n" + " addAll(values)\n" + "}"); + + WriteFieldAccessorDocComment(printer, descriptor_, LIST_INDEXED_SETTER, + /* builder */ false); + printer->Print( + variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"set$kt_capitalized_name$\")\n" + "operator fun com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>." + "set(index: kotlin.Int, value: $kt_type$) {\n" + " $kt_dsl_builder$.${$set$capitalized_name$$}$(index, value)\n" + "}"); + + WriteFieldAccessorDocComment(printer, descriptor_, CLEARER, + /* builder */ false); + printer->Print(variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"clear$kt_capitalized_name$\")\n" + "fun com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>." + "clear() {\n" + " $kt_dsl_builder$.${$clear$capitalized_name$$}$()\n" + "}"); +} + } // namespace java } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/java/java_message_field.h b/src/google/protobuf/compiler/java/java_message_field.h index 36fa49208cb..08c5e91bb23 100644 --- a/src/google/protobuf/compiler/java/java_message_field.h +++ b/src/google/protobuf/compiler/java/java_message_field.h @@ -81,6 +81,7 @@ class ImmutableMessageFieldGenerator : public ImmutableFieldGenerator { void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; void GenerateEqualsCode(io::Printer* printer) const; void GenerateHashCode(io::Printer* printer) const; + void GenerateKotlinDslMembers(io::Printer* printer) const; std::string GetBoxedType() const; @@ -146,6 +147,7 @@ class RepeatedImmutableMessageFieldGenerator : public ImmutableFieldGenerator { void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; void GenerateEqualsCode(io::Printer* printer) const; void GenerateHashCode(io::Printer* printer) const; + void GenerateKotlinDslMembers(io::Printer* printer) const; std::string GetBoxedType() const; diff --git a/src/google/protobuf/compiler/java/java_message_field_lite.cc b/src/google/protobuf/compiler/java/java_message_field_lite.cc index 73f1bcf5437..adb91a3616b 100644 --- a/src/google/protobuf/compiler/java/java_message_field_lite.cc +++ b/src/google/protobuf/compiler/java/java_message_field_lite.cc @@ -61,6 +61,7 @@ void SetMessageVariables(const FieldDescriptor* descriptor, int messageBitIndex, (*variables)["type"] = name_resolver->GetImmutableClassName(descriptor->message_type()); + (*variables)["kt_type"] = (*variables)["type"]; (*variables)["mutable_type"] = name_resolver->GetMutableClassName(descriptor->message_type()); (*variables)["group_or_message"] = @@ -70,6 +71,11 @@ void SetMessageVariables(const FieldDescriptor* descriptor, int messageBitIndex, // by the proto compiler (*variables)["deprecation"] = descriptor->options().deprecated() ? "@java.lang.Deprecated " : ""; + (*variables)["kt_deprecation"] = + descriptor->options().deprecated() + ? "@kotlin.Deprecated(message = \"Field " + (*variables)["name"] + + " is deprecated\") " + : ""; (*variables)["required"] = descriptor->is_required() ? "true" : "false"; if (HasHasbit(descriptor)) { @@ -277,6 +283,32 @@ void ImmutableMessageFieldLiteGenerator::GenerateBuilderMembers( printer->Annotate("{", "}", descriptor_); } +void ImmutableMessageFieldLiteGenerator::GenerateKotlinDslMembers( + io::Printer* printer) const { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$kt_deprecation$var $kt_name$: $kt_type$\n" + " @JvmName(\"${$get$kt_capitalized_name$$}$\")\n" + " get() = $kt_dsl_builder$.${$get$capitalized_name$$}$()\n" + " @JvmName(\"${$set$kt_capitalized_name$$}$\")\n" + " set(value) {\n" + " $kt_dsl_builder$.${$set$capitalized_name$$}$(value)\n" + " }\n"); + + WriteFieldAccessorDocComment(printer, descriptor_, CLEARER, + /* builder */ false); + printer->Print(variables_, + "fun ${$clear$kt_capitalized_name$$}$() {\n" + " $kt_dsl_builder$.${$clear$capitalized_name$$}$()\n" + "}\n"); + + WriteFieldAccessorDocComment(printer, descriptor_, HAZZER); + printer->Print(variables_, + "fun ${$has$kt_capitalized_name$$}$(): kotlin.Boolean {\n" + " return $kt_dsl_builder$.${$has$capitalized_name$$}$()\n" + "}\n"); +} + void ImmutableMessageFieldLiteGenerator::GenerateFieldInfo( io::Printer* printer, std::vector* output) const { WriteIntToUtf16CharSequence(descriptor_->number(), output); @@ -751,6 +783,98 @@ std::string RepeatedImmutableMessageFieldLiteGenerator::GetBoxedType() const { return name_resolver_->GetImmutableClassName(descriptor_->message_type()); } +void RepeatedImmutableMessageFieldLiteGenerator::GenerateKotlinDslMembers( + io::Printer* printer) const { + printer->Print( + variables_, + "/**\n" + " * An uninstantiable, behaviorless type to represent the field in\n" + " * generics.\n" + " */\n" + "@kotlin.OptIn" + "(com.google.protobuf.kotlin.OnlyForUseByGeneratedProtoCode::class)\n" + "class ${$$kt_capitalized_name$Proxy$}$ private constructor()" + " : com.google.protobuf.kotlin.DslProxy()\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$kt_deprecation$ val $kt_name$: " + "com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>\n" + " @kotlin.jvm.JvmSynthetic\n" + " get() = com.google.protobuf.kotlin.DslList(\n" + " $kt_dsl_builder$.${$get$capitalized_name$List$}$()\n" + " )\n"); + + WriteFieldAccessorDocComment(printer, descriptor_, LIST_ADDER, + /* builder */ false); + printer->Print(variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"add$kt_capitalized_name$\")\n" + "fun com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>." + "add(value: $kt_type$) {\n" + " $kt_dsl_builder$.${$add$capitalized_name$$}$(value)\n" + "}"); + + WriteFieldAccessorDocComment(printer, descriptor_, LIST_ADDER, + /* builder */ false); + printer->Print(variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"plusAssign$kt_capitalized_name$\")\n" + "inline operator fun com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>." + "plusAssign(value: $kt_type$) {\n" + " add(value)\n" + "}"); + + WriteFieldAccessorDocComment(printer, descriptor_, LIST_MULTI_ADDER, + /* builder */ false); + printer->Print(variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"addAll$kt_capitalized_name$\")\n" + "fun com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>." + "addAll(values: kotlin.collections.Iterable<$kt_type$>) {\n" + " $kt_dsl_builder$.${$addAll$capitalized_name$$}$(values)\n" + "}"); + + WriteFieldAccessorDocComment(printer, descriptor_, LIST_MULTI_ADDER, + /* builder */ false); + printer->Print( + variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"plusAssignAll$kt_capitalized_name$\")\n" + "inline operator fun com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>." + "plusAssign(values: kotlin.collections.Iterable<$kt_type$>) {\n" + " addAll(values)\n" + "}"); + + WriteFieldAccessorDocComment(printer, descriptor_, LIST_INDEXED_SETTER, + /* builder */ false); + printer->Print( + variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"set$kt_capitalized_name$\")\n" + "operator fun com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>." + "set(index: kotlin.Int, value: $kt_type$) {\n" + " $kt_dsl_builder$.${$set$capitalized_name$$}$(index, value)\n" + "}"); + + WriteFieldAccessorDocComment(printer, descriptor_, CLEARER, + /* builder */ false); + printer->Print(variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"clear$kt_capitalized_name$\")\n" + "fun com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>." + "clear() {\n" + " $kt_dsl_builder$.${$clear$capitalized_name$$}$()\n" + "}"); +} + } // namespace java } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/java/java_message_field_lite.h b/src/google/protobuf/compiler/java/java_message_field_lite.h index 8b935e68f9d..af0129c078f 100644 --- a/src/google/protobuf/compiler/java/java_message_field_lite.h +++ b/src/google/protobuf/compiler/java/java_message_field_lite.h @@ -73,6 +73,7 @@ class ImmutableMessageFieldLiteGenerator : public ImmutableFieldLiteGenerator { void GenerateInitializationCode(io::Printer* printer) const; void GenerateFieldInfo(io::Printer* printer, std::vector* output) const; + void GenerateKotlinDslMembers(io::Printer* printer) const; std::string GetBoxedType() const; @@ -118,6 +119,7 @@ class RepeatedImmutableMessageFieldLiteGenerator void GenerateInitializationCode(io::Printer* printer) const; void GenerateFieldInfo(io::Printer* printer, std::vector* output) const; + void GenerateKotlinDslMembers(io::Printer* printer) const; std::string GetBoxedType() const; diff --git a/src/google/protobuf/compiler/java/java_message_lite.cc b/src/google/protobuf/compiler/java/java_message_lite.cc index cae99637594..74ce6d62f47 100644 --- a/src/google/protobuf/compiler/java/java_message_lite.cc +++ b/src/google/protobuf/compiler/java/java_message_lite.cc @@ -724,6 +724,242 @@ 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( + "class Dsl private constructor(\n" + " @kotlin.jvm.JvmField private val _builder: $message$.Builder\n" + ") {\n" + " 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", 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 oneof : oneofs_) { + printer->Print( + "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" + "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", + name_resolver_->GetClassName(descriptor_, true)); + } + + if (descriptor_->extension_range_count() > 0) { + GenerateKotlinExtensions(printer); + } + + printer->Outdent(); + printer->Print("}\n"); +} + +void ImmutableMessageLiteGenerator::GenerateKotlinMembers( + io::Printer* printer) const { + printer->Print( + "@kotlin.jvm.JvmSynthetic\n" + "inline fun $camelcase_name$(block: $message_kt$.Dsl.() -> Unit): " + "$message$ =\n" + " $message_kt$.Dsl._create($message$.newBuilder()).apply { block() " + "}._build()\n", + "camelcase_name", name_resolver_->GetKotlinFactoryName(descriptor_), + "message_kt", name_resolver_->GetKotlinExtensionsClassName(descriptor_), + "message", name_resolver_->GetClassName(descriptor_, true)); + + printer->Print("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( + "inline fun $message$.copy(block: $message_kt$.Dsl.() -> Unit): " + "$message$ =\n" + " $message_kt$.Dsl._create(this.toBuilder()).apply { block() " + "}._build()\n", + "message", name_resolver_->GetClassName(descriptor_, true), "message_kt", + name_resolver_->GetKotlinExtensionsClassName(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); + } +} + +void ImmutableMessageLiteGenerator::GenerateKotlinExtensions( + io::Printer* printer) const { + std::string message_name = name_resolver_->GetClassName(descriptor_, true); + + printer->Print( + "@Suppress(\"UNCHECKED_CAST\")\n" + "@kotlin.jvm.JvmSynthetic\n" + "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$, " + "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" + "operator fun get(\n" + " extension: com.google.protobuf.ExtensionLite<$message$, 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" + "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" + "fun clear(extension: com.google.protobuf.ExtensionLite<$message$, *>) " + "{\n" + " _builder.clearExtension(extension)\n" + "}\n\n", + "message", message_name); + + printer->Print( + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.PublishedApi\n" + "internal 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" + "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" + "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" + "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" + "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" + "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" + "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" + "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" + "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" + "inline fun com.google.protobuf.kotlin.ExtensionList<*, " + "$message$>.clear() {\n" + " clear(extension)\n" + "}\n\n", + "message", message_name); +} + } // namespace java } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/java/java_message_lite.h b/src/google/protobuf/compiler/java/java_message_lite.h index 5290b1e6412..4dfa2913149 100644 --- a/src/google/protobuf/compiler/java/java_message_lite.h +++ b/src/google/protobuf/compiler/java/java_message_lite.h @@ -56,6 +56,9 @@ class ImmutableMessageLiteGenerator : public MessageGenerator { virtual void GenerateStaticVariables(io::Printer* printer, int* bytecode_estimate); virtual int GenerateStaticVariableInitializers(io::Printer* printer); + 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); @@ -66,6 +69,7 @@ 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; Context* context_; ClassNameResolver* name_resolver_; diff --git a/src/google/protobuf/compiler/java/java_name_resolver.cc b/src/google/protobuf/compiler/java/java_name_resolver.cc index ed33dae5ff5..43c7db53ccb 100644 --- a/src/google/protobuf/compiler/java/java_name_resolver.cc +++ b/src/google/protobuf/compiler/java/java_name_resolver.cc @@ -69,6 +69,16 @@ std::string ClassNameWithoutPackage(const Descriptor* descriptor, return StripPackageName(descriptor->full_name(), descriptor->file()); } +std::string ClassNameWithoutPackageKotlin(const Descriptor* descriptor) { + std::string result = descriptor->name(); + const Descriptor* temp = descriptor->containing_type(); + + while (temp) { + result = temp->name() + "Kt." + result; + temp = temp->containing_type(); + } + return result; +} // Get the name of an enum's Java class without package name prefix. std::string ClassNameWithoutPackage(const EnumDescriptor* descriptor, @@ -316,6 +326,12 @@ std::string ClassNameResolver::GetExtensionIdentifierName( descriptor->name(); } +std::string ClassNameResolver::GetKotlinFactoryName( + const Descriptor* descriptor) { + std::string name = ToCamelCase(descriptor->name(), /* lower_first = */ true); + return IsForbiddenKotlin(name) ? name + "_" : name; +} + std::string ClassNameResolver::GetJavaImmutableClassName( const Descriptor* descriptor) { return GetJavaClassFullName(ClassNameWithoutPackage(descriptor, true), @@ -328,6 +344,12 @@ std::string ClassNameResolver::GetJavaImmutableClassName( descriptor->file(), true); } +std::string ClassNameResolver::GetKotlinExtensionsClassName( + const Descriptor* descriptor) { + return GetClassFullName(ClassNameWithoutPackageKotlin(descriptor), + descriptor->file(), true, true, true); +} + } // namespace java } // namespace compiler diff --git a/src/google/protobuf/compiler/java/java_name_resolver.h b/src/google/protobuf/compiler/java/java_name_resolver.h index 8461df90097..9717d923279 100644 --- a/src/google/protobuf/compiler/java/java_name_resolver.h +++ b/src/google/protobuf/compiler/java/java_name_resolver.h @@ -115,6 +115,8 @@ class ClassNameResolver { // com.package.OuterClass$OuterMessage$InnerMessage std::string GetJavaImmutableClassName(const Descriptor* descriptor); std::string GetJavaImmutableClassName(const EnumDescriptor* descriptor); + std::string GetKotlinFactoryName(const Descriptor* descriptor); + std::string GetKotlinExtensionsClassName(const Descriptor* descriptor); private: // Get the full name of a Java class by prepending the Java package name // or outer class name. diff --git a/src/google/protobuf/compiler/java/java_primitive_field.cc b/src/google/protobuf/compiler/java/java_primitive_field.cc index 65cc05adcfc..2bc565c9105 100644 --- a/src/google/protobuf/compiler/java/java_primitive_field.cc +++ b/src/google/protobuf/compiler/java/java_primitive_field.cc @@ -68,6 +68,7 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, (*variables)["type"] = PrimitiveTypeName(javaType); (*variables)["boxed_type"] = BoxedPrimitiveTypeName(javaType); + (*variables)["kt_type"] = KotlinTypeName(javaType); (*variables)["field_type"] = (*variables)["type"]; if (javaType == JAVATYPE_BOOLEAN || javaType == JAVATYPE_DOUBLE || @@ -129,6 +130,11 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, // by the proto compiler (*variables)["deprecation"] = descriptor->options().deprecated() ? "@java.lang.Deprecated " : ""; + (*variables)["kt_deprecation"] = + descriptor->options().deprecated() + ? "@kotlin.Deprecated(message = \"Field " + (*variables)["name"] + + " is deprecated\") " + : ""; int fixed_size = FixedSize(GetType(descriptor)); if (fixed_size != -1) { (*variables)["fixed_size"] = StrCat(fixed_size); @@ -298,6 +304,33 @@ void ImmutablePrimitiveFieldGenerator::GenerateBuilderMembers( "}\n"); } +void ImmutablePrimitiveFieldGenerator::GenerateKotlinDslMembers( + io::Printer* printer) const { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$kt_deprecation$var $kt_name$: $kt_type$\n" + " @JvmName(\"${$get$kt_capitalized_name$$}$\")\n" + " get() = $kt_dsl_builder$.${$get$capitalized_name$$}$()\n" + " @JvmName(\"${$set$kt_capitalized_name$$}$\")\n" + " set(value) {\n" + " $kt_dsl_builder$.${$set$capitalized_name$$}$(value)\n" + " }\n"); + + WriteFieldAccessorDocComment(printer, descriptor_, CLEARER, + /* builder */ false); + printer->Print(variables_, + "fun ${$clear$kt_capitalized_name$$}$() {\n" + " $kt_dsl_builder$.${$clear$capitalized_name$$}$()\n" + "}\n"); + + if (HasHazzer(descriptor_)) { + WriteFieldAccessorDocComment(printer, descriptor_, HAZZER); + printer->Print(variables_, + "fun ${$has$kt_capitalized_name$$}$(): kotlin.Boolean {\n" + " return $kt_dsl_builder$.${$has$capitalized_name$$}$()\n" + "}\n"); + } +} void ImmutablePrimitiveFieldGenerator::GenerateFieldBuilderInitializationCode( io::Printer* printer) const { @@ -793,6 +826,98 @@ void RepeatedImmutablePrimitiveFieldGenerator::GenerateBuilderMembers( printer->Annotate("{", "}", descriptor_); } +void RepeatedImmutablePrimitiveFieldGenerator::GenerateKotlinDslMembers( + io::Printer* printer) const { + printer->Print( + variables_, + "/**\n" + " * An uninstantiable, behaviorless type to represent the field in\n" + " * generics.\n" + " */\n" + "@kotlin.OptIn" + "(com.google.protobuf.kotlin.OnlyForUseByGeneratedProtoCode::class)\n" + "class ${$$kt_capitalized_name$Proxy$}$ private constructor()" + " : com.google.protobuf.kotlin.DslProxy()\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$kt_deprecation$ val $kt_name$: " + "com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>\n" + " @kotlin.jvm.JvmSynthetic\n" + " get() = com.google.protobuf.kotlin.DslList(\n" + " $kt_dsl_builder$.${$get$capitalized_name$List$}$()\n" + " )\n"); + + WriteFieldAccessorDocComment(printer, descriptor_, LIST_ADDER, + /* builder */ false); + printer->Print(variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"add$kt_capitalized_name$\")\n" + "fun com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>." + "add(value: $kt_type$) {\n" + " $kt_dsl_builder$.${$add$capitalized_name$$}$(value)\n" + "}"); + + WriteFieldAccessorDocComment(printer, descriptor_, LIST_ADDER, + /* builder */ false); + printer->Print(variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"plusAssign$kt_capitalized_name$\")\n" + "inline operator fun com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>." + "plusAssign(value: $kt_type$) {\n" + " add(value)\n" + "}"); + + WriteFieldAccessorDocComment(printer, descriptor_, LIST_MULTI_ADDER, + /* builder */ false); + printer->Print(variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"addAll$kt_capitalized_name$\")\n" + "fun com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>." + "addAll(values: kotlin.collections.Iterable<$kt_type$>) {\n" + " $kt_dsl_builder$.${$addAll$capitalized_name$$}$(values)\n" + "}"); + + WriteFieldAccessorDocComment(printer, descriptor_, LIST_MULTI_ADDER, + /* builder */ false); + printer->Print( + variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"plusAssignAll$kt_capitalized_name$\")\n" + "inline operator fun com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>." + "plusAssign(values: kotlin.collections.Iterable<$kt_type$>) {\n" + " addAll(values)\n" + "}"); + + WriteFieldAccessorDocComment(printer, descriptor_, LIST_INDEXED_SETTER, + /* builder */ false); + printer->Print( + variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"set$kt_capitalized_name$\")\n" + "operator fun com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>." + "set(index: kotlin.Int, value: $kt_type$) {\n" + " $kt_dsl_builder$.${$set$capitalized_name$$}$(index, value)\n" + "}"); + + WriteFieldAccessorDocComment(printer, descriptor_, CLEARER, + /* builder */ false); + printer->Print(variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"clear$kt_capitalized_name$\")\n" + "fun com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>." + "clear() {\n" + " $kt_dsl_builder$.${$clear$capitalized_name$$}$()\n" + "}"); +} + void RepeatedImmutablePrimitiveFieldGenerator:: GenerateFieldBuilderInitializationCode(io::Printer* printer) const { // noop for primitives diff --git a/src/google/protobuf/compiler/java/java_primitive_field.h b/src/google/protobuf/compiler/java/java_primitive_field.h index db20750e262..56be916e2e2 100644 --- a/src/google/protobuf/compiler/java/java_primitive_field.h +++ b/src/google/protobuf/compiler/java/java_primitive_field.h @@ -81,6 +81,7 @@ class ImmutablePrimitiveFieldGenerator : public ImmutableFieldGenerator { void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; void GenerateEqualsCode(io::Printer* printer) const; void GenerateHashCode(io::Printer* printer) const; + void GenerateKotlinDslMembers(io::Printer* printer) const; std::string GetBoxedType() const; @@ -139,6 +140,7 @@ class RepeatedImmutablePrimitiveFieldGenerator void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; void GenerateEqualsCode(io::Printer* printer) const; void GenerateHashCode(io::Printer* printer) const; + void GenerateKotlinDslMembers(io::Printer* printer) const; std::string GetBoxedType() const; diff --git a/src/google/protobuf/compiler/java/java_primitive_field_lite.cc b/src/google/protobuf/compiler/java/java_primitive_field_lite.cc index 1932e99a279..cc589f33ea3 100644 --- a/src/google/protobuf/compiler/java/java_primitive_field_lite.cc +++ b/src/google/protobuf/compiler/java/java_primitive_field_lite.cc @@ -74,6 +74,7 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, JavaType javaType = GetJavaType(descriptor); (*variables)["type"] = PrimitiveTypeName(javaType); (*variables)["boxed_type"] = BoxedPrimitiveTypeName(javaType); + (*variables)["kt_type"] = KotlinTypeName(javaType); (*variables)["field_type"] = (*variables)["type"]; (*variables)["default"] = ImmutableDefaultValue(descriptor, name_resolver); (*variables)["capitalized_type"] = @@ -137,6 +138,11 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, // by the proto compiler (*variables)["deprecation"] = descriptor->options().deprecated() ? "@java.lang.Deprecated " : ""; + (*variables)["kt_deprecation"] = + descriptor->options().deprecated() + ? "@kotlin.Deprecated(message = \"Field " + (*variables)["name"] + + " is deprecated\") " + : ""; int fixed_size = FixedSize(GetType(descriptor)); if (fixed_size != -1) { (*variables)["fixed_size"] = StrCat(fixed_size); @@ -303,6 +309,33 @@ void ImmutablePrimitiveFieldLiteGenerator::GenerateBuilderMembers( printer->Annotate("{", "}", descriptor_); } +void ImmutablePrimitiveFieldLiteGenerator::GenerateKotlinDslMembers( + io::Printer* printer) const { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$kt_deprecation$var $kt_name$: $kt_type$\n" + " @JvmName(\"${$get$kt_capitalized_name$$}$\")\n" + " get() = $kt_dsl_builder$.${$get$capitalized_name$$}$()\n" + " @JvmName(\"${$set$kt_capitalized_name$$}$\")\n" + " set(value) {\n" + " $kt_dsl_builder$.${$set$capitalized_name$$}$(value)\n" + " }\n"); + + WriteFieldAccessorDocComment(printer, descriptor_, CLEARER, + /* builder */ false); + printer->Print(variables_, + "fun ${$clear$kt_capitalized_name$$}$() {\n" + " $kt_dsl_builder$.${$clear$capitalized_name$$}$()\n" + "}\n"); + + if (HasHazzer(descriptor_)) { + WriteFieldAccessorDocComment(printer, descriptor_, HAZZER); + printer->Print(variables_, + "fun ${$has$kt_capitalized_name$$}$(): kotlin.Boolean {\n" + " return $kt_dsl_builder$.${$has$capitalized_name$$}$()\n" + "}\n"); + } +} void ImmutablePrimitiveFieldLiteGenerator::GenerateFieldInfo( io::Printer* printer, std::vector* output) const { @@ -615,6 +648,98 @@ void RepeatedImmutablePrimitiveFieldLiteGenerator::GenerateBuilderMembers( printer->Annotate("{", "}", descriptor_); } +void RepeatedImmutablePrimitiveFieldLiteGenerator::GenerateKotlinDslMembers( + io::Printer* printer) const { + printer->Print( + variables_, + "/**\n" + " * An uninstantiable, behaviorless type to represent the field in\n" + " * generics.\n" + " */\n" + "@kotlin.OptIn" + "(com.google.protobuf.kotlin.OnlyForUseByGeneratedProtoCode::class)\n" + "class ${$$kt_capitalized_name$Proxy$}$ private constructor()" + " : com.google.protobuf.kotlin.DslProxy()\n"); + + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$kt_deprecation$ val $kt_name$: " + "com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>\n" + " @kotlin.jvm.JvmSynthetic\n" + " get() = com.google.protobuf.kotlin.DslList(\n" + " $kt_dsl_builder$.${$get$capitalized_name$List$}$()\n" + " )\n"); + + WriteFieldAccessorDocComment(printer, descriptor_, LIST_ADDER, + /* builder */ false); + printer->Print(variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"add$kt_capitalized_name$\")\n" + "fun com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>." + "add(value: $kt_type$) {\n" + " $kt_dsl_builder$.${$add$capitalized_name$$}$(value)\n" + "}"); + + WriteFieldAccessorDocComment(printer, descriptor_, LIST_ADDER, + /* builder */ false); + printer->Print(variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"plusAssign$kt_capitalized_name$\")\n" + "inline operator fun com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>." + "plusAssign(value: $kt_type$) {\n" + " add(value)\n" + "}"); + + WriteFieldAccessorDocComment(printer, descriptor_, LIST_MULTI_ADDER, + /* builder */ false); + printer->Print(variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"addAll$kt_capitalized_name$\")\n" + "fun com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>." + "addAll(values: kotlin.collections.Iterable<$kt_type$>) {\n" + " $kt_dsl_builder$.${$addAll$capitalized_name$$}$(values)\n" + "}"); + + WriteFieldAccessorDocComment(printer, descriptor_, LIST_MULTI_ADDER, + /* builder */ false); + printer->Print( + variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"plusAssignAll$kt_capitalized_name$\")\n" + "inline operator fun com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>." + "plusAssign(values: kotlin.collections.Iterable<$kt_type$>) {\n" + " addAll(values)\n" + "}"); + + WriteFieldAccessorDocComment(printer, descriptor_, LIST_INDEXED_SETTER, + /* builder */ false); + printer->Print( + variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"set$kt_capitalized_name$\")\n" + "operator fun com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>." + "set(index: kotlin.Int, value: $kt_type$) {\n" + " $kt_dsl_builder$.${$set$capitalized_name$$}$(index, value)\n" + "}"); + + WriteFieldAccessorDocComment(printer, descriptor_, CLEARER, + /* builder */ false); + printer->Print(variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"clear$kt_capitalized_name$\")\n" + "fun com.google.protobuf.kotlin.DslList" + "<$kt_type$, ${$$kt_capitalized_name$Proxy$}$>." + "clear() {\n" + " $kt_dsl_builder$.${$clear$capitalized_name$$}$()\n" + "}"); +} + void RepeatedImmutablePrimitiveFieldLiteGenerator::GenerateFieldInfo( io::Printer* printer, std::vector* output) const { WriteIntToUtf16CharSequence(descriptor_->number(), output); diff --git a/src/google/protobuf/compiler/java/java_primitive_field_lite.h b/src/google/protobuf/compiler/java/java_primitive_field_lite.h index 22a25579a79..b9fcacd29af 100644 --- a/src/google/protobuf/compiler/java/java_primitive_field_lite.h +++ b/src/google/protobuf/compiler/java/java_primitive_field_lite.h @@ -73,6 +73,7 @@ class ImmutablePrimitiveFieldLiteGenerator void GenerateInitializationCode(io::Printer* printer) const; void GenerateFieldInfo(io::Printer* printer, std::vector* output) const; + void GenerateKotlinDslMembers(io::Printer* printer) const; std::string GetBoxedType() const; @@ -119,7 +120,8 @@ class RepeatedImmutablePrimitiveFieldLiteGenerator void GenerateInitializationCode(io::Printer* printer) const; void GenerateFieldInfo(io::Printer* printer, std::vector* output) const; - + void GenerateKotlinDslMembers(io::Printer* printer) const; + std::string GetBoxedType() const; private: diff --git a/src/google/protobuf/compiler/java/java_string_field.cc b/src/google/protobuf/compiler/java/java_string_field.cc index 2e9a9e789a0..8d72d9530af 100644 --- a/src/google/protobuf/compiler/java/java_string_field.cc +++ b/src/google/protobuf/compiler/java/java_string_field.cc @@ -90,6 +90,11 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, // by the proto compiler (*variables)["deprecation"] = descriptor->options().deprecated() ? "@java.lang.Deprecated " : ""; + (*variables)["kt_deprecation"] = + descriptor->options().deprecated() + ? "@kotlin.Deprecated(message = \"Field " + (*variables)["name"] + + " is deprecated\") " + : ""; (*variables)["on_changed"] = "onChanged();"; if (HasHasbit(descriptor)) { @@ -367,6 +372,34 @@ void ImmutableStringFieldGenerator::GenerateBuilderMembers( "}\n"); } +void ImmutableStringFieldGenerator::GenerateKotlinDslMembers( + io::Printer* printer) const { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$kt_deprecation$var $kt_name$: kotlin.String\n" + " @JvmName(\"${$get$kt_capitalized_name$$}$\")\n" + " get() = $kt_dsl_builder$.${$get$capitalized_name$$}$()\n" + " @JvmName(\"${$set$kt_capitalized_name$$}$\")\n" + " set(value) {\n" + " $kt_dsl_builder$.${$set$capitalized_name$$}$(value)\n" + " }\n"); + + WriteFieldAccessorDocComment(printer, descriptor_, CLEARER, + /* builder */ false); + printer->Print(variables_, + "fun ${$clear$kt_capitalized_name$$}$() {\n" + " $kt_dsl_builder$.${$clear$capitalized_name$$}$()\n" + "}\n"); + + if (HasHazzer(descriptor_)) { + WriteFieldAccessorDocComment(printer, descriptor_, HAZZER); + printer->Print(variables_, + "fun ${$has$kt_capitalized_name$$}$(): kotlin.Boolean {\n" + " return $kt_dsl_builder$.${$has$capitalized_name$$}$()\n" + "}\n"); + } +} + void ImmutableStringFieldGenerator::GenerateFieldBuilderInitializationCode( io::Printer* printer) const { // noop for primitives @@ -916,6 +949,107 @@ void RepeatedImmutableStringFieldGenerator::GenerateBuilderMembers( "}\n"); } +void RepeatedImmutableStringFieldGenerator::GenerateKotlinDslMembers( + io::Printer* printer) const { + printer->Print( + variables_, + "/**\n" + " * An uninstantiable, behaviorless type to represent the field in\n" + " * generics.\n" + " */\n" + "@kotlin.OptIn" + "(com.google.protobuf.kotlin.OnlyForUseByGeneratedProtoCode::class)\n" + "class ${$$kt_capitalized_name$Proxy$}$ private constructor()" + " : com.google.protobuf.kotlin.DslProxy()\n"); + + // property for List + WriteFieldAccessorDocComment(printer, descriptor_, LIST_GETTER); + printer->Print( + variables_, + "val $kt_name$: " + "com.google.protobuf.kotlin.DslList" + "\n" + " @kotlin.OptIn" + "(com.google.protobuf.kotlin.OnlyForUseByGeneratedProtoCode::class)\n" + " get() = com.google.protobuf.kotlin.DslList(\n" + " $kt_dsl_builder$.${$get$capitalized_name$List$}$()\n" + " )\n"); + + // List.add(String) + WriteFieldAccessorDocComment(printer, descriptor_, LIST_ADDER, + /* builder */ false); + printer->Print(variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"add$kt_capitalized_name$\")\n" + "fun com.google.protobuf.kotlin.DslList" + "." + "add(value: kotlin.String) {\n" + " $kt_dsl_builder$.${$add$capitalized_name$$}$(value)\n" + "}\n"); + + // List += String + WriteFieldAccessorDocComment(printer, descriptor_, LIST_ADDER, + /* builder */ false); + printer->Print(variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"plusAssign$kt_capitalized_name$\")\n" + "operator fun com.google.protobuf.kotlin.DslList" + "." + "plusAssign(value: kotlin.String) {\n" + " $kt_dsl_builder$.${$add$capitalized_name$$}$(value)\n" + "}\n"); + + // List.addAll(Iterable) + WriteFieldAccessorDocComment(printer, descriptor_, LIST_MULTI_ADDER, + /* builder */ false); + printer->Print( + variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"addAll$kt_capitalized_name$\")\n" + "fun com.google.protobuf.kotlin.DslList" + "." + "addAll(values: kotlin.collections.Iterable) {\n" + " $kt_dsl_builder$.${$addAll$capitalized_name$$}$(values)\n" + "}\n"); + + // List += Iterable + WriteFieldAccessorDocComment(printer, descriptor_, LIST_MULTI_ADDER, + /* builder */ false); + printer->Print( + variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"plusAssignAll$kt_capitalized_name$\")\n" + "operator fun com.google.protobuf.kotlin.DslList" + "." + "plusAssign(values: kotlin.collections.Iterable) {\n" + " $kt_dsl_builder$.${$addAll$capitalized_name$$}$(values)\n" + "}\n"); + + // List[Int] = String + WriteFieldAccessorDocComment(printer, descriptor_, LIST_INDEXED_SETTER, + /* builder */ false); + printer->Print( + variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"set$kt_capitalized_name$\")\n" + "operator fun com.google.protobuf.kotlin.DslList" + "." + "set(index: kotlin.Int, value: kotlin.String) {\n" + " $kt_dsl_builder$.${$set$capitalized_name$$}$(index, value)\n" + "}"); + + WriteFieldAccessorDocComment(printer, descriptor_, CLEARER, + /* builder */ false); + printer->Print(variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"clear$kt_capitalized_name$\")\n" + "fun com.google.protobuf.kotlin.DslList" + "." + "clear() {\n" + " $kt_dsl_builder$.${$clear$capitalized_name$$}$()\n" + "}"); +} + void RepeatedImmutableStringFieldGenerator:: GenerateFieldBuilderInitializationCode(io::Printer* printer) const { // noop for primitives diff --git a/src/google/protobuf/compiler/java/java_string_field.h b/src/google/protobuf/compiler/java/java_string_field.h index 1c00ae81c29..6cc91260cf1 100644 --- a/src/google/protobuf/compiler/java/java_string_field.h +++ b/src/google/protobuf/compiler/java/java_string_field.h @@ -81,6 +81,7 @@ class ImmutableStringFieldGenerator : public ImmutableFieldGenerator { void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; void GenerateEqualsCode(io::Printer* printer) const; void GenerateHashCode(io::Printer* printer) const; + void GenerateKotlinDslMembers(io::Printer* printer) const; std::string GetBoxedType() const; @@ -137,6 +138,7 @@ class RepeatedImmutableStringFieldGenerator : public ImmutableFieldGenerator { void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; void GenerateEqualsCode(io::Printer* printer) const; void GenerateHashCode(io::Printer* printer) const; + void GenerateKotlinDslMembers(io::Printer* printer) const; std::string GetBoxedType() const; diff --git a/src/google/protobuf/compiler/java/java_string_field_lite.cc b/src/google/protobuf/compiler/java/java_string_field_lite.cc index 63702c19927..f22fc4b5fd4 100644 --- a/src/google/protobuf/compiler/java/java_string_field_lite.cc +++ b/src/google/protobuf/compiler/java/java_string_field_lite.cc @@ -86,6 +86,11 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, // by the proto compiler (*variables)["deprecation"] = descriptor->options().deprecated() ? "@java.lang.Deprecated " : ""; + (*variables)["kt_deprecation"] = + descriptor->options().deprecated() + ? "@kotlin.Deprecated(message = \"Field " + (*variables)["name"] + + " is deprecated\") " + : ""; (*variables)["required"] = descriptor->is_required() ? "true" : "false"; if (HasHasbit(descriptor)) { @@ -304,6 +309,34 @@ void ImmutableStringFieldLiteGenerator::GenerateBuilderMembers( printer->Annotate("{", "}", descriptor_); } +void ImmutableStringFieldLiteGenerator::GenerateKotlinDslMembers( + io::Printer* printer) const { + WriteFieldDocComment(printer, descriptor_); + printer->Print(variables_, + "$kt_deprecation$var $kt_name$: kotlin.String\n" + " @JvmName(\"${$get$kt_capitalized_name$$}$\")\n" + " get() = $kt_dsl_builder$.${$get$capitalized_name$$}$()\n" + " @JvmName(\"${$set$kt_capitalized_name$$}$\")\n" + " set(value) {\n" + " $kt_dsl_builder$.${$set$capitalized_name$$}$(value)\n" + " }\n"); + + WriteFieldAccessorDocComment(printer, descriptor_, CLEARER, + /* builder */ false); + printer->Print(variables_, + "fun ${$clear$kt_capitalized_name$$}$() {\n" + " $kt_dsl_builder$.${$clear$capitalized_name$$}$()\n" + "}\n"); + + if (HasHazzer(descriptor_)) { + WriteFieldAccessorDocComment(printer, descriptor_, HAZZER); + printer->Print(variables_, + "fun ${$has$kt_capitalized_name$$}$(): kotlin.Boolean {\n" + " return $kt_dsl_builder$.${$has$capitalized_name$$}$()\n" + "}\n"); + } +} + void ImmutableStringFieldLiteGenerator::GenerateFieldInfo( io::Printer* printer, std::vector* output) const { WriteIntToUtf16CharSequence(descriptor_->number(), output); @@ -704,6 +737,107 @@ void RepeatedImmutableStringFieldLiteGenerator::GenerateBuilderMembers( printer->Annotate("{", "}", descriptor_); } +void RepeatedImmutableStringFieldLiteGenerator::GenerateKotlinDslMembers( + io::Printer* printer) const { + printer->Print( + variables_, + "/**\n" + " * An uninstantiable, behaviorless type to represent the field in\n" + " * generics.\n" + " */\n" + "@kotlin.OptIn" + "(com.google.protobuf.kotlin.OnlyForUseByGeneratedProtoCode::class)\n" + "class ${$$kt_capitalized_name$Proxy$}$ private constructor()" + " : com.google.protobuf.kotlin.DslProxy()\n"); + + // property for List + WriteFieldAccessorDocComment(printer, descriptor_, LIST_GETTER); + printer->Print( + variables_, + "val $kt_name$: " + "com.google.protobuf.kotlin.DslList" + "\n" + " @kotlin.OptIn" + "(com.google.protobuf.kotlin.OnlyForUseByGeneratedProtoCode::class)\n" + " get() = com.google.protobuf.kotlin.DslList(\n" + " $kt_dsl_builder$.${$get$capitalized_name$List$}$()\n" + " )\n"); + + // List.add(String) + WriteFieldAccessorDocComment(printer, descriptor_, LIST_ADDER, + /* builder */ false); + printer->Print(variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"add$kt_capitalized_name$\")\n" + "fun com.google.protobuf.kotlin.DslList" + "." + "add(value: kotlin.String) {\n" + " $kt_dsl_builder$.${$add$capitalized_name$$}$(value)\n" + "}\n"); + + // List += String + WriteFieldAccessorDocComment(printer, descriptor_, LIST_ADDER, + /* builder */ false); + printer->Print(variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"plusAssign$kt_capitalized_name$\")\n" + "inline operator fun com.google.protobuf.kotlin.DslList" + "." + "plusAssign(value: kotlin.String) {\n" + " add(value)\n" + "}\n"); + + // List.addAll(Iterable) + WriteFieldAccessorDocComment(printer, descriptor_, LIST_MULTI_ADDER, + /* builder */ false); + printer->Print( + variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"addAll$kt_capitalized_name$\")\n" + "fun com.google.protobuf.kotlin.DslList" + "." + "addAll(values: kotlin.collections.Iterable) {\n" + " $kt_dsl_builder$.${$addAll$capitalized_name$$}$(values)\n" + "}\n"); + + // List += Iterable + WriteFieldAccessorDocComment(printer, descriptor_, LIST_MULTI_ADDER, + /* builder */ false); + printer->Print( + variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"plusAssignAll$kt_capitalized_name$\")\n" + "inline operator fun com.google.protobuf.kotlin.DslList" + "." + "plusAssign(values: kotlin.collections.Iterable) {\n" + " addAll(values)\n" + "}\n"); + + // List[Int] = String + WriteFieldAccessorDocComment(printer, descriptor_, LIST_INDEXED_SETTER, + /* builder */ false); + printer->Print( + variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"set$kt_capitalized_name$\")\n" + "operator fun com.google.protobuf.kotlin.DslList" + "." + "set(index: kotlin.Int, value: kotlin.String) {\n" + " $kt_dsl_builder$.${$set$capitalized_name$$}$(index, value)\n" + "}"); + + WriteFieldAccessorDocComment(printer, descriptor_, CLEARER, + /* builder */ false); + printer->Print(variables_, + "@kotlin.jvm.JvmSynthetic\n" + "@kotlin.jvm.JvmName(\"clear$kt_capitalized_name$\")\n" + "fun com.google.protobuf.kotlin.DslList" + "." + "clear() {\n" + " $kt_dsl_builder$.${$clear$capitalized_name$$}$()\n" + "}"); +} + void RepeatedImmutableStringFieldLiteGenerator::GenerateFieldInfo( io::Printer* printer, std::vector* output) const { WriteIntToUtf16CharSequence(descriptor_->number(), output); diff --git a/src/google/protobuf/compiler/java/java_string_field_lite.h b/src/google/protobuf/compiler/java/java_string_field_lite.h index 194a19ddc24..c95256f4f2c 100644 --- a/src/google/protobuf/compiler/java/java_string_field_lite.h +++ b/src/google/protobuf/compiler/java/java_string_field_lite.h @@ -74,6 +74,7 @@ class ImmutableStringFieldLiteGenerator : public ImmutableFieldLiteGenerator { void GenerateInitializationCode(io::Printer* printer) const; void GenerateFieldInfo(io::Printer* printer, std::vector* output) const; + void GenerateKotlinDslMembers(io::Printer* printer) const; std::string GetBoxedType() const; @@ -118,6 +119,7 @@ class RepeatedImmutableStringFieldLiteGenerator void GenerateInitializationCode(io::Printer* printer) const; void GenerateFieldInfo(io::Printer* printer, std::vector* output) const; + void GenerateKotlinDslMembers(io::Printer* printer) const; std::string GetBoxedType() const; diff --git a/src/google/protobuf/compiler/main.cc b/src/google/protobuf/compiler/main.cc index 895b47dee65..7cb7a6375cd 100644 --- a/src/google/protobuf/compiler/main.cc +++ b/src/google/protobuf/compiler/main.cc @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -64,6 +65,10 @@ int ProtobufMain(int argc, char* argv[]) { cli.RegisterGenerator("--java_out", "--java_opt", &java_generator, "Generate Java source file."); + // Proto2 Kotlin + java::KotlinGenerator kt_generator; + cli.RegisterGenerator("--kotlin_out", "--kotlin_opt", &kt_generator, + "Generate Kotlin file."); // Proto2 Python