diff --git a/java/core/src/test/java/com/google/protobuf/kotlin/AndroidManifest.xml b/java/core/src/test/java/com/google/protobuf/kotlin/AndroidManifest.xml new file mode 100644 index 0000000000..56a4551aa3 --- /dev/null +++ b/java/core/src/test/java/com/google/protobuf/kotlin/AndroidManifest.xml @@ -0,0 +1,9 @@ + + + + + diff --git a/java/core/src/test/java/com/google/protobuf/kotlin/BUILD b/java/core/src/test/java/com/google/protobuf/kotlin/BUILD new file mode 100644 index 0000000000..f81b7f3905 --- /dev/null +++ b/java/core/src/test/java/com/google/protobuf/kotlin/BUILD @@ -0,0 +1,248 @@ +# Tests for the Kotlin proto runtime. +load("//tools/build_defs/android:rules.bzl", "android_binary") +load("//tools/build_defs/kotlin:rules.bzl", "kt_jvm_library") +load("//testing/build_defs:junit_test_suites.bzl", "junit_test_suites") +load( + "//third_party/protobuf/build_defs:kt_jvm_proto_library.bzl", + "kt_jvm_lite_proto_library", + "kt_jvm_proto_library", +) + +proto_library( + name = "test_proto", + srcs = ["test.proto"], +) + +java_proto_library( + name = "test_java_proto", + deps = [":test_proto"], +) + +java_lite_proto_library( + name = "test_java_proto_lite", + deps = [":test_proto"], +) + +kt_jvm_library( + name = "shared_tests", + srcs = [ + "DslListTest.kt", + "DslMapTest.kt", + "ExtensionListTest.kt", + ], + kotlincopts = ["-Xopt-in=kotlin.RequiresOptIn"], + deps = [ + ":test_java_proto_lite", + "//java/com/google/common/testing", + "//java/com/google/protobuf/kotlin", + "//java/com/google/protobuf/kotlin:only_for_use_in_proto_generated_code_its_generator_and_tests", + "//java/com/google/protobuf/kotlin:shared_runtime", + "//third_party/java/junit", + "//third_party/java/truth", + "//third_party/kotlin/kotlin:kotlin_test", + ], +) + +junit_test_suites( + name = "shared_test_suite", + sizes = ["small"], + suffix = "Shared", + deps = [":shared_tests"], +) + +proto_library( + name = "evil_names_proto2", + srcs = ["evil_names_proto2.proto"], +) + +proto_library( + name = "evil_names_proto3", + srcs = ["evil_names_proto3.proto"], +) + +proto_library( + name = "multiple_files_proto3", + srcs = ["multiple_files_proto3.proto"], +) + +kt_jvm_lite_proto_library( + name = "proto2_unittest_kt_jvm_proto_lite", + deps = [ + ":evil_names_proto2", + "//net/proto2/internal:unittest_lite_proto", + ], +) + +kt_jvm_proto_library( + name = "proto2_unittest_kt_jvm_proto", + deps = [ + ":evil_names_proto2", + "//net/proto2/internal:unittest_proto", + ], +) + +kt_jvm_lite_proto_library( + name = "proto3_unittest_kt_jvm_proto_lite", + deps = [ + ":evil_names_proto3", + ":multiple_files_proto3", + "//net/proto2/internal:unittest_proto3", + ], +) + +kt_jvm_proto_library( + name = "proto3_unittest_kt_jvm_proto", + deps = [ + ":evil_names_proto3", + ":multiple_files_proto3", + "//net/proto2/internal:unittest_proto3", + ], +) + +kt_jvm_library( + name = "proto2_lite_test", + srcs = ["Proto2LiteTest.kt"], + constraints = ["android"], + kotlincopts = [ + "-language-version", + "1.3", + ], + deps = [ + ":proto2_unittest_kt_jvm_proto_lite", + "//java/com/google/protobuf/kotlin:shared_runtime", + "//javatests/com/google/protobuf:test_util-android", + "//third_party/java/junit:junit-android", + "//third_party/java/truth:truth-android", + "//third_party/kotlin/kotlin:kotlin_test", + ], +) + +kt_jvm_library( + name = "proto2_test", + srcs = ["Proto2Test.kt"], + kotlincopts = [ + "-language-version", + "1.3", + ], + deps = [ + "proto2_unittest_kt_jvm_proto", + "//java/com/google/protobuf/kotlin:shared_runtime", + "//javatests/com/google/protobuf:test_util", + "//net/proto2/internal:unittest_java_proto", + "//third_party/java/junit", + "//third_party/java/truth", + "//third_party/kotlin/kotlin:kotlin_test", + ], +) + +kt_jvm_library( + name = "proto3_lite_test", + srcs = ["Proto3Test.kt"], + kotlincopts = [ + "-language-version", + "1.3", + ], + deps = [ + ":proto3_unittest_kt_jvm_proto_lite", + "//java/com/google/protobuf/kotlin:shared_runtime", + "//net/proto2/internal:unittest_proto3_java_proto_lite", + "//third_party/java/junit", + "//third_party/java/truth", + "//third_party/kotlin/kotlin:kotlin_test", + ], +) + +kt_jvm_library( + name = "proto3_test", + srcs = ["Proto3Test.kt"], + kotlincopts = [ + "-language-version", + "1.3", + ], + deps = [ + ":proto3_unittest_kt_jvm_proto", + "//java/com/google/protobuf/kotlin:shared_runtime", + "//net/proto2/internal:unittest_proto3_java_proto", + "//third_party/java/junit", + "//third_party/java/truth", + "//third_party/kotlin/kotlin:kotlin_test", + ], +) + +junit_test_suites( + name = "proto2_lite_tests_junit", + sizes = ["small"], + suffix = "Proto2LiteGeneratedCode", + deps = [":proto2_lite_test"], +) + +junit_test_suites( + name = "proto2_tests_junit", + sizes = ["small"], + suffix = "Proto2GeneratedCode", + deps = [":proto2_test"], +) + +junit_test_suites( + name = "generated_proto3_lite", + sizes = ["small"], + suffix = "Proto3LiteGeneratedCode", + deps = ["proto3_lite_test"], +) + +junit_test_suites( + name = "generated_full_protos", + sizes = ["small"], + suffix = "Proto3GeneratedCode", + deps = [":proto3_test"], +) + +kt_jvm_library( + name = "lite_runtime_tests_lib", + srcs = ["ExtendableMessageLiteExtensionsTest.kt"], + constraints = ["android"], + deps = [ + ":test_java_proto_lite", + "//java/com/google/protobuf/kotlin:kotlin_lite", + "//third_party/java/junit:junit-android", + "//third_party/java/truth:truth-android", + ], +) + +junit_test_suites( + name = "lite_runtime_tests", + sizes = ["small"], + suffix = "LiteRuntime", + deps = [":lite_runtime_tests_lib"], +) + +kt_jvm_library( + name = "full_runtime_tests_lib", + srcs = ["ExtendableMessageExtensionsTest.kt"], + deps = [ + ":test_java_proto", + "//java/com/google/protobuf/kotlin", + "//third_party/java/junit", + "//third_party/java/truth", + ], +) + +junit_test_suites( + name = "full_runtime_tests", + sizes = ["small"], + suffix = "FullRuntime", + deps = [":full_runtime_tests_lib"], +) + +# Generate a binary with AppReduce run on it, to verify no choking. The proto runtime includes +# kt_proto.pgcfg, which enforces that the DSLs are stripped out, so we just want to make sure +# that's run. +android_binary( + name = "Proto2LiteTestBinary", + manifest = "AndroidManifest.xml", + proguard_specs = [ + "//java/com/google/android/apps/common/proguard:base.pgcfg", + "//java/com/google/android/apps/common/proguard:dev_optimize.pgcfg", + ], + deps = [":proto2_lite_test"], +) diff --git a/java/core/src/test/java/com/google/protobuf/kotlin/DslListTest.kt b/java/core/src/test/java/com/google/protobuf/kotlin/DslListTest.kt new file mode 100644 index 0000000000..e5e6017c06 --- /dev/null +++ b/java/core/src/test/java/com/google/protobuf/kotlin/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/core/src/test/java/com/google/protobuf/kotlin/DslMapTest.kt b/java/core/src/test/java/com/google/protobuf/kotlin/DslMapTest.kt new file mode 100644 index 0000000000..470e42e48a --- /dev/null +++ b/java/core/src/test/java/com/google/protobuf/kotlin/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/core/src/test/java/com/google/protobuf/kotlin/ExtendableMessageExtensionsTest.kt b/java/core/src/test/java/com/google/protobuf/kotlin/ExtendableMessageExtensionsTest.kt new file mode 100644 index 0000000000..f4c7e4e3f0 --- /dev/null +++ b/java/core/src/test/java/com/google/protobuf/kotlin/ExtendableMessageExtensionsTest.kt @@ -0,0 +1,60 @@ +package com.google.protobuf.kotlin + +import com.google.common.truth.Truth.assertThat +import com.google.protobuf.kotlin.test.ExampleExtensibleMessage +import com.google.protobuf.kotlin.test.Test 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/core/src/test/java/com/google/protobuf/kotlin/ExtendableMessageLiteExtensionsTest.kt b/java/core/src/test/java/com/google/protobuf/kotlin/ExtendableMessageLiteExtensionsTest.kt new file mode 100644 index 0000000000..9cf8c5975f --- /dev/null +++ b/java/core/src/test/java/com/google/protobuf/kotlin/ExtendableMessageLiteExtensionsTest.kt @@ -0,0 +1,60 @@ +package com.google.protobuf.kotlin + +import com.google.common.truth.Truth.assertThat +import com.google.protobuf.kotlin.test.ExampleExtensibleMessage +import com.google.protobuf.kotlin.test.Test 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/core/src/test/java/com/google/protobuf/kotlin/ExtensionListTest.kt b/java/core/src/test/java/com/google/protobuf/kotlin/ExtensionListTest.kt new file mode 100644 index 0000000000..9a82a5cddb --- /dev/null +++ b/java/core/src/test/java/com/google/protobuf/kotlin/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 com.google.protobuf.kotlin.test.ExampleExtensibleMessage +import com.google.protobuf.kotlin.test.Test 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/core/src/test/java/com/google/protobuf/kotlin/Proto2LiteTest.kt b/java/core/src/test/java/com/google/protobuf/kotlin/Proto2LiteTest.kt new file mode 100644 index 0000000000..470d978353 --- /dev/null +++ b/java/core/src/test/java/com/google/protobuf/kotlin/Proto2LiteTest.kt @@ -0,0 +1,988 @@ +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 com.google.protobuf.kotlin.generator.EvilNamesProto2OuterClass.EvilNamesProto2 +import com.google.protobuf.kotlin.generator.EvilNamesProto2OuterClass.HardKeywordsAllTypes +import com.google.protobuf.kotlin.generator.EvilNamesProto2OuterClass.Interface +import com.google.protobuf.kotlin.generator.HardKeywordsAllTypesKt +import com.google.protobuf.kotlin.generator.evilNamesProto2 +import com.google.protobuf.kotlin.generator.hardKeywordsAllTypes +import com.google.protobuf.kotlin.generator.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 com.google.protos.proto2_unittest.MapLiteUnittest.MapEnumLite +import com.google.protos.proto2_unittest.MapLiteUnittest.TestMapLite +import com.google.protos.proto2_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] += 301 + this[UnittestLite.repeatedInt64ExtensionLite] += 302L + this[UnittestLite.repeatedUint32ExtensionLite] += 303 + this[UnittestLite.repeatedUint64ExtensionLite] += 304L + this[UnittestLite.repeatedSint32ExtensionLite] += 305 + this[UnittestLite.repeatedSint64ExtensionLite] += 306L + this[UnittestLite.repeatedFixed32ExtensionLite] += 307 + this[UnittestLite.repeatedFixed64ExtensionLite] += 308L + this[UnittestLite.repeatedSfixed32ExtensionLite] += 309 + this[UnittestLite.repeatedSfixed64ExtensionLite] += 310L + this[UnittestLite.repeatedFloatExtensionLite] += 311F + this[UnittestLite.repeatedDoubleExtensionLite] += 312.0 + this[UnittestLite.repeatedBoolExtensionLite] += false + this[UnittestLite.repeatedStringExtensionLite] += "315" + this[UnittestLite.repeatedBytesExtensionLite] += toBytes("316") + this[UnittestLite.repeatedGroupExtensionLite] += repeatedGroupExtensionLite { a = 317 } + this[UnittestLite.repeatedNestedMessageExtensionLite] += + TestAllTypesLiteKt.nestedMessage { bb = 318 } + this[UnittestLite.repeatedForeignMessageExtensionLite] += foreignMessageLite { c = 319 } + this[UnittestLite.repeatedImportMessageExtensionLite] += + ImportMessageLite.newBuilder().setD(320).build() + this[UnittestLite.repeatedLazyMessageExtensionLite] += + TestAllTypesLiteKt.nestedMessage { bb = 327 } + this[UnittestLite.repeatedNestedEnumExtensionLite] += NestedEnum.BAZ + this[UnittestLite.repeatedForeignEnumExtensionLite] += ForeignEnumLite.FOREIGN_LITE_BAZ + this[UnittestLite.repeatedImportEnumExtensionLite] += ImportEnumLite.IMPORT_LITE_BAZ + this[UnittestLite.repeatedStringPieceExtensionLite] += "324" + this[UnittestLite.repeatedCordExtensionLite] += "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] += 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] += 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] += + 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] += + 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] += 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/core/src/test/java/com/google/protobuf/kotlin/Proto2Test.kt b/java/core/src/test/java/com/google/protobuf/kotlin/Proto2Test.kt new file mode 100644 index 0000000000..e2c471ba59 --- /dev/null +++ b/java/core/src/test/java/com/google/protobuf/kotlin/Proto2Test.kt @@ -0,0 +1,980 @@ +package com.google.protobuf.kotlin + +import com.google.common.truth.Truth.assertThat +import com.google.protobuf.TestUtil +import com.google.protobuf.TestUtil.toBytes +import com.google.protobuf.kotlin.generator.EvilNamesProto2OuterClass.EvilNamesProto2 +import com.google.protobuf.kotlin.generator.EvilNamesProto2OuterClass.HardKeywordsAllTypes +import com.google.protobuf.kotlin.generator.EvilNamesProto2OuterClass.Interface +import com.google.protobuf.kotlin.generator.HardKeywordsAllTypesKt +import com.google.protobuf.kotlin.generator.evilNamesProto2 +import com.google.protobuf.kotlin.generator.hardKeywordsAllTypes +import com.google.protobuf.kotlin.generator.interface_ +import com.google.protobuf.test.UnittestImport.ImportEnum +import com.google.protobuf.test.UnittestImport.ImportMessage +import com.google.protobuf.test.UnittestImportPublic.PublicImportMessage +import com.google.protos.proto2_unittest.MapProto2Unittest.Proto2MapEnum +import com.google.protos.proto2_unittest.MapProto2Unittest.TestEnumMap +import com.google.protos.proto2_unittest.MapProto2Unittest.TestIntIntMap +import com.google.protos.proto2_unittest.MapProto2Unittest.TestMaps +import com.google.protos.proto2_unittest.TestAllTypesKt +import com.google.protos.proto2_unittest.TestAllTypesKt.nestedMessage +import com.google.protos.proto2_unittest.UnittestProto +import com.google.protos.proto2_unittest.UnittestProto.ForeignEnum +import com.google.protos.proto2_unittest.UnittestProto.TestAllTypes +import com.google.protos.proto2_unittest.UnittestProto.TestAllTypes.NestedEnum +import com.google.protos.proto2_unittest.UnittestProto.TestEmptyMessage +import com.google.protos.proto2_unittest.UnittestProto.TestEmptyMessageWithExtensions +import com.google.protos.proto2_unittest.copy +import com.google.protos.proto2_unittest.foreignMessage +import com.google.protos.proto2_unittest.optionalGroupExtension +import com.google.protos.proto2_unittest.repeatedGroupExtension +import com.google.protos.proto2_unittest.testAllExtensions +import com.google.protos.proto2_unittest.testAllTypes +import com.google.protos.proto2_unittest.testEmptyMessage +import com.google.protos.proto2_unittest.testEmptyMessageWithExtensions +import com.google.protos.proto2_unittest.testEnumMap +import com.google.protos.proto2_unittest.testIntIntMap +import com.google.protos.proto2_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] += 301 + this[UnittestProto.repeatedInt64Extension] += 302L + this[UnittestProto.repeatedUint32Extension] += 303 + this[UnittestProto.repeatedUint64Extension] += 304L + this[UnittestProto.repeatedSint32Extension] += 305 + this[UnittestProto.repeatedSint64Extension] += 306L + this[UnittestProto.repeatedFixed32Extension] += 307 + this[UnittestProto.repeatedFixed64Extension] += 308L + this[UnittestProto.repeatedSfixed32Extension] += 309 + this[UnittestProto.repeatedSfixed64Extension] += 310L + this[UnittestProto.repeatedFloatExtension] += 311F + this[UnittestProto.repeatedDoubleExtension] += 312.0 + this[UnittestProto.repeatedBoolExtension] += false + this[UnittestProto.repeatedStringExtension] += "315" + this[UnittestProto.repeatedBytesExtension] += toBytes("316") + this[UnittestProto.repeatedGroupExtension] += repeatedGroupExtension { a = 317 } + this[UnittestProto.repeatedNestedMessageExtension] += + TestAllTypesKt.nestedMessage { bb = 318 } + this[UnittestProto.repeatedForeignMessageExtension] += foreignMessage { c = 319 } + this[UnittestProto.repeatedImportMessageExtension] += + ImportMessage.newBuilder().setD(320).build() + this[UnittestProto.repeatedLazyMessageExtension] += + TestAllTypesKt.nestedMessage { bb = 327 } + this[UnittestProto.repeatedNestedEnumExtension] += NestedEnum.BAZ + this[UnittestProto.repeatedForeignEnumExtension] += ForeignEnum.FOREIGN_BAZ + this[UnittestProto.repeatedImportEnumExtension] += ImportEnum.IMPORT_BAZ + this[UnittestProto.repeatedStringPieceExtension] += "324" + this[UnittestProto.repeatedCordExtension] += "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 testRepeatedExtensionGettersAndSetters() { + testAllExtensions { + this[UnittestProto.repeatedInt32Extension].addAll(listOf(1, 2)) + assertThat(this[UnittestProto.repeatedInt32Extension]).isEqualTo(listOf(1, 2)) + this[UnittestProto.repeatedInt32Extension] += listOf(3, 4) + assertThat(this[UnittestProto.repeatedInt32Extension]).isEqualTo(listOf(1, 2, 3, 4)) + this[UnittestProto.repeatedInt32Extension][0] = 5 + assertThat(this[UnittestProto.repeatedInt32Extension]).isEqualTo(listOf(5, 2, 3, 4)) + + this[UnittestProto.repeatedStringExtension].addAll(listOf("1", "2")) + assertThat(this[UnittestProto.repeatedStringExtension]).isEqualTo(listOf("1", "2")) + this[UnittestProto.repeatedStringExtension] += listOf("3", "4") + assertThat(this[UnittestProto.repeatedStringExtension]).isEqualTo(listOf("1", "2", "3", "4")) + this[UnittestProto.repeatedStringExtension][0] = "5" + assertThat(this[UnittestProto.repeatedStringExtension]).isEqualTo(listOf("5", "2", "3", "4")) + + this[UnittestProto.repeatedGroupExtension].addAll( + listOf( + repeatedGroupExtension { a = 1 }, + repeatedGroupExtension { a = 2 } + ) + ) + assertThat(this[UnittestProto.repeatedGroupExtension]).isEqualTo( + listOf( + repeatedGroupExtension { a = 1 }, + repeatedGroupExtension { a = 2 } + ) + ) + this[UnittestProto.repeatedGroupExtension] += + listOf( + repeatedGroupExtension { a = 3 }, + repeatedGroupExtension { a = 4 } + ) + assertThat(this[UnittestProto.repeatedGroupExtension]).isEqualTo( + listOf( + repeatedGroupExtension { a = 1 }, + repeatedGroupExtension { a = 2 }, + repeatedGroupExtension { a = 3 }, + repeatedGroupExtension { a = 4 } + ) + ) + this[UnittestProto.repeatedGroupExtension][0] = repeatedGroupExtension { a = 5 } + assertThat(this[UnittestProto.repeatedGroupExtension]).isEqualTo( + listOf( + repeatedGroupExtension { a = 5 }, + repeatedGroupExtension { a = 2 }, + repeatedGroupExtension { a = 3 }, + repeatedGroupExtension { a = 4 } + ) + ) + + this[UnittestProto.repeatedNestedMessageExtension].addAll( + listOf(nestedMessage { bb = 1 }, nestedMessage { bb = 2 }) + ) + assertThat(this[UnittestProto.repeatedNestedMessageExtension]).isEqualTo( + listOf(nestedMessage { bb = 1 }, nestedMessage { bb = 2 }) + ) + this[UnittestProto.repeatedNestedMessageExtension] += + listOf(nestedMessage { bb = 3 }, nestedMessage { bb = 4 }) + assertThat(this[UnittestProto.repeatedNestedMessageExtension]).isEqualTo( + listOf( + nestedMessage { bb = 1 }, + nestedMessage { bb = 2 }, + nestedMessage { bb = 3 }, + nestedMessage { bb = 4 } + ) + ) + this[UnittestProto.repeatedNestedMessageExtension][0] = nestedMessage { bb = 5 } + assertThat(this[UnittestProto.repeatedNestedMessageExtension]).isEqualTo( + listOf( + nestedMessage { bb = 5 }, + nestedMessage { bb = 2 }, + nestedMessage { bb = 3 }, + nestedMessage { bb = 4 } + ) + ) + + this[UnittestProto.repeatedNestedEnumExtension] + .addAll(listOf(NestedEnum.FOO, NestedEnum.BAR)) + assertThat(this[UnittestProto.repeatedNestedEnumExtension]) + .isEqualTo(listOf(NestedEnum.FOO, NestedEnum.BAR)) + this[UnittestProto.repeatedNestedEnumExtension] += listOf(NestedEnum.BAZ, NestedEnum.FOO) + assertThat(this[UnittestProto.repeatedNestedEnumExtension]).isEqualTo( + listOf(NestedEnum.FOO, NestedEnum.BAR, NestedEnum.BAZ, NestedEnum.FOO) + ) + } + } + + @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/core/src/test/java/com/google/protobuf/kotlin/Proto3Test.kt b/java/core/src/test/java/com/google/protobuf/kotlin/Proto3Test.kt new file mode 100644 index 0000000000..b134307ce0 --- /dev/null +++ b/java/core/src/test/java/com/google/protobuf/kotlin/Proto3Test.kt @@ -0,0 +1,335 @@ +package com.google.protobuf.kotlin + +import com.google.common.truth.Truth.assertThat +import com.google.protobuf.kotlin.generator.EvilNamesProto3OuterClass.Class +import com.google.protobuf.kotlin.generator.EvilNamesProto3OuterClass.EvilNamesProto3 +import com.google.protobuf.kotlin.generator.EvilNamesProto3OuterClass.HardKeywordsAllTypes +import com.google.protobuf.kotlin.generator.HardKeywordsAllTypesKt +import com.google.protobuf.kotlin.generator.class_ +import com.google.protobuf.kotlin.generator.evilNamesProto3 +import com.google.protobuf.kotlin.generator.hardKeywordsAllTypes +import com.google.protos.proto3_unittest.TestAllTypesKt +import com.google.protos.proto3_unittest.TestAllTypesKt.nestedMessage +import com.google.protos.proto3_unittest.UnittestProto3.TestAllTypes +import com.google.protos.proto3_unittest.UnittestProto3.TestAllTypes.NestedEnum +import com.google.protos.proto3_unittest.UnittestProto3.TestEmptyMessage +import com.google.protos.proto3_unittest.copy +import com.google.protos.proto3_unittest.testAllTypes +import com.google.protos.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( + com.google.protobuf.kotlin.generator.multipleFilesMessageA {} + ).isEqualTo( + com.google.protobuf.kotlin.generator.MultipleFilesMessageA.newBuilder().build() + ) + + assertThat( + com.google.protobuf.kotlin.generator.multipleFilesMessageB {} + ).isEqualTo( + com.google.protobuf.kotlin.generator.MultipleFilesMessageB.newBuilder().build() + ) + } +} diff --git a/java/core/src/test/java/com/google/protobuf/kotlin/evil_names_proto2.proto b/java/core/src/test/java/com/google/protobuf/kotlin/evil_names_proto2.proto new file mode 100644 index 0000000000..a462abd827 --- /dev/null +++ b/java/core/src/test/java/com/google/protobuf/kotlin/evil_names_proto2.proto @@ -0,0 +1,63 @@ +// LINT: LEGACY_NAMES +syntax = "proto2"; + +package protobuf.kotlin.generator; + +option java_package = "com.google.protobuf.kotlin.generator"; + +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/core/src/test/java/com/google/protobuf/kotlin/evil_names_proto3.proto b/java/core/src/test/java/com/google/protobuf/kotlin/evil_names_proto3.proto new file mode 100644 index 0000000000..f0faa1c10c --- /dev/null +++ b/java/core/src/test/java/com/google/protobuf/kotlin/evil_names_proto3.proto @@ -0,0 +1,77 @@ +// LINT: LEGACY_NAMES +syntax = "proto3"; + +package protobuf.kotlin.generator; + +option java_package = "com.google.protobuf.kotlin.generator"; + +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/core/src/test/java/com/google/protobuf/kotlin/multiple_files_proto3.proto b/java/core/src/test/java/com/google/protobuf/kotlin/multiple_files_proto3.proto new file mode 100644 index 0000000000..1b14213291 --- /dev/null +++ b/java/core/src/test/java/com/google/protobuf/kotlin/multiple_files_proto3.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +package protobuf.kotlin.generator; + +option java_package = "com.google.protobuf.kotlin.generator"; +option java_multiple_files = true; + +enum NestedEnum { FOO = 0; } + +message MultipleFilesMessageA {} + +message MultipleFilesMessageB {} diff --git a/java/core/src/test/java/com/google/protobuf/kotlin/test.proto b/java/core/src/test/java/com/google/protobuf/kotlin/test.proto new file mode 100644 index 0000000000..7416eb45a8 --- /dev/null +++ b/java/core/src/test/java/com/google/protobuf/kotlin/test.proto @@ -0,0 +1,16 @@ +syntax = "proto2"; + +package protobuf.kotlin.test; + +option java_package = "com.google.protobuf.kotlin.test"; +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/src/google/protobuf/compiler/java/java_enum_field.cc b/src/google/protobuf/compiler/java/java_enum_field.cc index 9706c6d027..2ebfd67dc7 100644 --- a/src/google/protobuf/compiler/java/java_enum_field.cc +++ b/src/google/protobuf/compiler/java/java_enum_field.cc @@ -61,6 +61,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); @@ -74,6 +75,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. @@ -268,6 +274,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 @@ -1035,6 +1069,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 95c7db578f..a8cd033cf1 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 dfa051c16d..b78469d1b6 100644 --- a/src/google/protobuf/compiler/java/java_enum_field_lite.cc +++ b/src/google/protobuf/compiler/java/java_enum_field_lite.cc @@ -68,6 +68,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); @@ -81,6 +82,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)) { @@ -273,6 +279,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_)) { @@ -772,6 +806,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 b5e9807728..75760ecaef 100644 --- a/src/google/protobuf/compiler/java/java_enum_field_lite.h +++ b/src/google/protobuf/compiler/java/java_enum_field_lite.h @@ -71,6 +71,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; @@ -116,6 +117,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 2f775a68a6..db5a332d8b 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 9d04dc8455..2e125dccfc 100644 --- a/src/google/protobuf/compiler/java/java_field.h +++ b/src/google/protobuf/compiler/java/java_field.h @@ -83,6 +83,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; @@ -105,6 +106,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 5cdb00d5c1..e8801a10b9 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 9f1f719237..71ee3e8557 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 3aebf72c7e..16e297af12 100644 --- a/src/google/protobuf/compiler/java/java_helpers.cc +++ b/src/google/protobuf/compiler/java/java_helpers.cc @@ -90,6 +90,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]) { @@ -216,6 +227,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); @@ -422,6 +437,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. + } + + 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 8cc2f5af80..4a925ba0db 100644 --- a/src/google/protobuf/compiler/java/java_helpers.h +++ b/src/google/protobuf/compiler/java/java_helpers.h @@ -51,6 +51,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 @@ -214,6 +215,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 0000000000..fce3a8cf93 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_kotlin_generator.cc @@ -0,0 +1,130 @@ +#include "net/proto2/compiler/java/public/kotlin_generator.h" + +#include "net/proto2/compiler/java/internal/file.h" +#include "net/proto2/compiler/java/internal/helpers.h" +#include "net/proto2/compiler/java/internal/options.h" +#include "net/proto2/compiler/java/public/generator.h" +#include "net/proto2/compiler/public/code_generator.h" + +namespace proto2 { +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; + if (file_options.generate_immutable_code) { + file_generator = absl::make_unique( + 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 proto2 diff --git a/src/google/protobuf/compiler/java/java_map_field.cc b/src/google/protobuf/compiler/java/java_map_field.cc index 5db199d38f..a4ec2ffad7 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 2ff1f7673e..63b2577003 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 4fa939f3b8..11b85b2757 100644 --- a/src/google/protobuf/compiler/java/java_map_field_lite.cc +++ b/src/google/protobuf/compiler/java/java_map_field_lite.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())); @@ -90,6 +101,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 @@ -131,6 +144,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"; @@ -788,6 +806,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 49cbf6cca7..2d59fdc64f 100644 --- a/src/google/protobuf/compiler/java/java_map_field_lite.h +++ b/src/google/protobuf/compiler/java/java_map_field_lite.h @@ -53,6 +53,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 f9d4e43ff8..c0fc49945c 100644 --- a/src/google/protobuf/compiler/java/java_message.cc +++ b/src/google/protobuf/compiler/java/java_message.cc @@ -1356,6 +1356,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 87b9df5e59..50d718039a 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 96c0c11276..ad49586f38 100644 --- a/src/google/protobuf/compiler/java/java_message_field.cc +++ b/src/google/protobuf/compiler/java/java_message_field.cc @@ -59,6 +59,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"] = @@ -68,6 +69,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"] = @@ -406,6 +412,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_)) { @@ -1360,6 +1392,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 36fa49208c..08c5e91bb2 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 b17859d6b3..35897ea650 100644 --- a/src/google/protobuf/compiler/java/java_message_field_lite.cc +++ b/src/google/protobuf/compiler/java/java_message_field_lite.cc @@ -59,6 +59,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"] = @@ -68,6 +69,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)) { @@ -275,6 +281,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); @@ -749,6 +781,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 c0a9b37f4f..51b53dcb53 100644 --- a/src/google/protobuf/compiler/java/java_message_field_lite.h +++ b/src/google/protobuf/compiler/java/java_message_field_lite.h @@ -71,6 +71,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; @@ -116,6 +117,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 4afffdc7a9..6431540b63 100644 --- a/src/google/protobuf/compiler/java/java_message_lite.cc +++ b/src/google/protobuf/compiler/java/java_message_lite.cc @@ -723,6 +723,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 5290b1e641..4dfa291314 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 ed33dae5ff..43c7db53cc 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 8461df9009..9717d92327 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 8bc68b7b22..6f0efeffd3 100644 --- a/src/google/protobuf/compiler/java/java_primitive_field.cc +++ b/src/google/protobuf/compiler/java/java_primitive_field.cc @@ -66,6 +66,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 || @@ -127,6 +128,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); @@ -296,6 +302,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 { @@ -791,6 +824,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 db20750e26..56be916e2e 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 e807066247..58be7446ce 100644 --- a/src/google/protobuf/compiler/java/java_primitive_field_lite.cc +++ b/src/google/protobuf/compiler/java/java_primitive_field_lite.cc @@ -73,6 +73,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"] = @@ -135,6 +136,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); @@ -301,6 +307,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 { @@ -613,6 +646,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 5867cee746..fcdee9c452 100644 --- a/src/google/protobuf/compiler/java/java_primitive_field_lite.h +++ b/src/google/protobuf/compiler/java/java_primitive_field_lite.h @@ -71,6 +71,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; @@ -117,6 +118,7 @@ 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; diff --git a/src/google/protobuf/compiler/java/java_string_field.cc b/src/google/protobuf/compiler/java/java_string_field.cc index 485fcf812e..c3bf45d171 100644 --- a/src/google/protobuf/compiler/java/java_string_field.cc +++ b/src/google/protobuf/compiler/java/java_string_field.cc @@ -88,6 +88,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)) { @@ -365,6 +370,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 @@ -914,6 +947,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 1c00ae81c2..6cc91260cf 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 25bfedcae2..5f6e27af67 100644 --- a/src/google/protobuf/compiler/java/java_string_field_lite.cc +++ b/src/google/protobuf/compiler/java/java_string_field_lite.cc @@ -83,6 +83,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)) { @@ -301,6 +306,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); @@ -701,6 +734,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 b67135cd81..1f3dafc50f 100644 --- a/src/google/protobuf/compiler/java/java_string_field_lite.h +++ b/src/google/protobuf/compiler/java/java_string_field_lite.h @@ -72,6 +72,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; @@ -116,6 +117,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/third_party/build_defs/BUILD b/third_party/build_defs/BUILD new file mode 100644 index 0000000000..15cf88cca6 --- /dev/null +++ b/third_party/build_defs/BUILD @@ -0,0 +1,20 @@ +load("//tools/build_defs/testing:bzl_library.bzl", "bzl_library") + +licenses(["notice"]) + +bzl_library( + name = "kt_jvm_proto_library_helper", + srcs = ["kt_jvm_proto_library.internal.bzl"], + visibility = ["//visibility:private"], + deps = [ + "//tools/build_defs/kotlin/release/rules:android_jvm_compile_bzl", + "//tools/build_defs/kotlin/release/toolchains/kotlin_jvm:toolchain_bzl", + ], +) + +bzl_library( + name = "kt_jvm_proto_library", + srcs = ["kt_jvm_proto_library.bzl"], + visibility = ["//visibility:private"], + deps = [":kt_jvm_proto_library_helper"], +) diff --git a/third_party/build_defs/kt_jvm_proto_library.bzl b/third_party/build_defs/kt_jvm_proto_library.bzl new file mode 100644 index 0000000000..5e5115fe28 --- /dev/null +++ b/third_party/build_defs/kt_jvm_proto_library.bzl @@ -0,0 +1,140 @@ +"""Definitions for Kotlin proto libraries.""" + +load(":kt_jvm_proto_library.internal.bzl", _kt_proto_library_helper = "kt_proto_library_helper") +load("//devtools/build_cleaner/skylark:build_defs.bzl", "register_extension_info") + +def _lite_proto_name(name): + return ":%s_DO_NOT_DEPEND_java_lite_proto" % name + +def _proto_name(name): + return ":%s_DO_NOT_DEPEND_java_proto" % name + +def kt_jvm_lite_proto_library( + name, + deps = None, + tags = None, + testonly = None, + compatible_with = None, + restricted_to = None, + visibility = None, + deprecation = None, + features = []): + """ + This rule generates and compiles lite Java and Kotlin APIs for a specified proto_library. + + Args: + name: A name for the target + deps: One or more proto_library targets + tags: List of string tags passed to generated targets. + testonly: Whether this target is intended only for tests. + compatible_with: Standard attribute, see http://go/be-common#common.compatible_with + restricted_to: Standard attribute, see http://go/be-common#common.restricted_to + visibility: A list of targets allowed to depend on this rule. + deprecation: Standard attribute, see http://go/be-common#common.deprecation + features: Features enabled. + """ + + tags = (tags or []) + ["kt_jvm_lite_proto_library"] + + native.java_lite_proto_library( + name = _lite_proto_name(name)[1:], + deps = deps, + testonly = testonly, + compatible_with = compatible_with, + visibility = ["//visibility:private"], + restricted_to = restricted_to, + tags = tags + ["avoid_dep"], + deprecation = deprecation, + features = features, + ) + + _kt_proto_library_helper( + name = name, + proto_deps = deps, + deps = [ + _lite_proto_name(name), + "//java/com/google/protobuf:protobuf_lite", + "//java/com/google/protobuf/kotlin:only_for_use_in_proto_generated_code_its_generator_and_tests", + "//java/com/google/protobuf/kotlin:shared_runtime", + ], + exports = [_lite_proto_name(name)], + testonly = testonly, + compatible_with = compatible_with, + restricted_to = restricted_to, + constraints = ["android"], + tags = tags, + variant = "lite", + visibility = visibility, + deprecation = deprecation, + features = features, + ) + +def kt_jvm_proto_library( + name, + deps = None, + tags = None, + testonly = None, + compatible_with = None, + restricted_to = None, + visibility = None, + deprecation = None, + features = []): + """ + This rule generates and compiles full Java and Kotlin APIs for a specified proto_library. + + Args: + name: A name for the target + deps: One or more proto_library targets + tags: List of string tags passed to generated targets. + testonly: Whether this target is intended only for tests. + compatible_with: Standard attribute, see http://go/be-common#common.compatible_with + restricted_to: Standard attribute, see http://go/be-common#common.restricted_to + visibility: A list of targets allowed to depend on this rule. + deprecation: Standard attribute, see http://go/be-common#common.deprecation + features: Features enabled. + """ + + tags = (tags or []) + ["kt_jvm_proto_library"] + + native.java_proto_library( + name = _proto_name(name)[1:], + deps = deps, + testonly = testonly, + compatible_with = compatible_with, + visibility = ["//visibility:private"], + restricted_to = restricted_to, + tags = tags + ["avoid_dep"], + deprecation = deprecation, + features = features, + ) + + _kt_proto_library_helper( + name = name, + proto_deps = deps, + deps = [ + _proto_name(name), + "//java/com/google/protobuf", + "//java/com/google/protobuf/kotlin:only_for_use_in_proto_generated_code_its_generator_and_tests", + "//java/com/google/protobuf/kotlin:shared_runtime", + ], + exports = [_proto_name(name)], + testonly = testonly, + compatible_with = compatible_with, + constraints = [], + restricted_to = restricted_to, + tags = tags, + variant = "full", + visibility = visibility, + deprecation = deprecation, + features = features, + ) + +register_extension_info( + extension = kt_jvm_proto_library, + label_regex_for_dep = "{extension_name}", +) + +register_extension_info( + extension = kt_jvm_lite_proto_library, + label_regex_for_dep = "{extension_name}", +) diff --git a/third_party/build_defs/kt_jvm_proto_library.internal.bzl b/third_party/build_defs/kt_jvm_proto_library.internal.bzl new file mode 100644 index 0000000000..fcb2cd1537 --- /dev/null +++ b/third_party/build_defs/kt_jvm_proto_library.internal.bzl @@ -0,0 +1,177 @@ +"""Definitions for internals of Kotlin proto libraries.""" + +load("//tools/build_defs/kotlin/release/rules:common.bzl", "common") +load( + "//tools/build_defs/kotlin/release/rules:dep_checks_aspect.bzl", + "KtForbiddenInfo", + "dep_checks_aspect", +) +load( + "//tools/build_defs/kotlin/release/toolchains/kotlin_jvm:toolchain.bzl", + _toolchain = "toolchain", +) + +def _get_real_short_path(file): + """Returns the correct short path file name to be used by protoc.""" + short_path = file.short_path + if short_path.startswith("../"): + second_slash = short_path.index("/", 3) + short_path = short_path[second_slash + 1:] + + virtual_imports = "_virtual_imports/" + if virtual_imports in short_path: + short_path = short_path.split(virtual_imports)[1].split("/", 1)[1] + return short_path + +def _kt_proto_extensions_impl(ctx): + for dep in ctx.attr.proto_deps + ctx.attr.deps + ctx.attr.exports: + if dep[KtForbiddenInfo].forbidden: + fail("""Not allowed to depend on %s from Kotlin code, see go/kotlin/build-rules#restrictions: + %s""" % (dep.label, "\n".join(dep[KtForbiddenInfo].forbidden))) + + transitive_set = depset( + transitive = + [dep[ProtoInfo].transitive_descriptor_sets for dep in ctx.attr.proto_deps], + ) + proto_sources = [] + for dep in ctx.attr.proto_deps: + for file in dep[ProtoInfo].direct_sources: + proto_sources.append(_get_real_short_path(file)) + + gen_src_dir = ctx.actions.declare_directory(ctx.label.name + "/ktproto") + codegen_params = "lite:" if ctx.attr.variant == "lite" else "" + + protoc_args = ctx.actions.args() + protoc_args.add("--kotlin_out=" + codegen_params + gen_src_dir.path) + protoc_args.add_joined( + transitive_set, + join_with = ctx.configuration.host_path_separator, + format_joined = "--descriptor_set_in=%s", + ) + protoc_args.add_all(proto_sources) + + ctx.actions.run( + inputs = depset(transitive = [transitive_set]), + outputs = [gen_src_dir], + executable = ctx.executable._protoc, + arguments = [protoc_args], + progress_message = "Generating kotlin proto extensions for " + + ", ".join([ + str(dep.label) + for dep in ctx.attr.proto_deps + ]), + ) + + kt_toolchain = _toolchain.get(ctx) + compiler_plugins = [ + kt_toolchain.marker_plugin, + kt_toolchain.strictdeps_plugin, + ] + result = common.kt_jvm_library( + ctx, + srcs = [gen_src_dir], + output = ctx.outputs.jar, + deps = [dep[JavaInfo] for dep in ctx.attr.deps], + exports = [dep[JavaInfo] for dep in ctx.attr.exports], + kotlincopts = ["-Xopt-in=kotlin.RequiresOptIn", "-nowarn"], + kt_toolchain = kt_toolchain, + java_toolchain = ctx.attr._java_toolchain, + compiler_plugins = compiler_plugins, + android_compatible = "android" in ctx.attr.constraints, + enforce_strict_deps = False, + ) + java_info = java_common.add_constraints( + result.java_info, + constraints = ctx.attr.constraints, + ) + + transitive_runfiles = [] + for p in common.collect_providers(DefaultInfo, ctx.attr.deps + ctx.attr.exports): + transitive_runfiles.append(p.data_runfiles.files) + transitive_runfiles.append(p.default_runfiles.files) + runfiles = ctx.runfiles( + files = [ctx.outputs.jar], + transitive_files = depset(transitive = transitive_runfiles), + collect_default = True, + ) + + return [ + java_info, + OutputGroupInfo(_validation = depset(result.validations)), + DefaultInfo( + files = depset([ctx.outputs.jar]), + runfiles = runfiles, + ), + ProguardSpecProvider(depset(ctx.files._proguard_specs)), + ] + +kt_proto_library_helper = rule( + doc = """ + This is a helper rule to combine the kotlin code generation work + of kt_jvm_proto_library and kt_jvm_lite_proto_library. + + It generates Kotlin proto code with the protoc compiler using the + --kotlin_out flag and compiles that Kotlin code. It requires as a dependency + a proto_library and either a java_proto_library or java_lite_proto_library. + """, + attrs = dict( + proto_deps = attr.label_list( + providers = [ProtoInfo], + aspects = [dep_checks_aspect], + ), + deps = attr.label_list( + providers = [JavaInfo], + aspects = [dep_checks_aspect], + ), + exports = attr.label_list( + allow_rules = ["java_proto_library", "java_lite_proto_library"], + aspects = [dep_checks_aspect], + ), + constraints = attr.string_list( + doc = """Extra constraints imposed on this library, most commonly, 'android'. Constraints + typically indicate that the library may be used in binaries running outside of + Google production services. See go/be-java#java_library.constraints for details on + supported constraints and what they mean. Note that all Java and Kotlin `deps` are + required to carry compatible constraints.""", + ), + variant = attr.string( + mandatory = True, + values = ["lite", "full"], + doc = """Indicator of which Kotlin proto runtime to use.""", + ), + _protoc = attr.label( + default = Label("//net/proto2/compiler/public:protocol_compiler"), + cfg = "host", + executable = True, + ), + _java_toolchain = attr.label( + default = Label( + "//tools/jdk:current_java_toolchain", + ), + ), + _coverage_instrumenter = attr.label( + default = Label("//tools/build_defs/kotlin/release/rules:kt_coverage_instrumenter"), + cfg = "host", + executable = True, + ), + _coverage_runtime = attr.label( + default = Label("//third_party/java/jacoco:blaze-agent"), + ), + _toolchain = attr.label( + default = Label( + "//tools/build_defs/kotlin/release/toolchains/kotlin_jvm:kt_jvm_toolchain_internal_DO_NOT_USE", + ), + ), + _proguard_specs = attr.label_list( + allow_files = [".pgcfg"], + default = ["//java/com/google/protobuf/kotlin:kt_proto.pgcfg"], + ), + ), + fragments = ["java"], + outputs = dict( + jar = "lib%{name}.ktproto.jar", + ), + provides = [JavaInfo], + implementation = _kt_proto_extensions_impl, + toolchains = [_toolchain.type], +)