From 84d08867a49c92293624fb4f0e143a6567b23065 Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Thu, 20 Mar 2025 14:23:00 -0700 Subject: [PATCH 1/6] add kotlin equality and tests --- packages/pigeon/CHANGELOG.md | 4 + .../EventChannelMessages.g.kt | 24 +++ .../flutter/pigeon_example_app/Messages.g.kt | 38 ++++ packages/pigeon/lib/src/generator_tools.dart | 2 +- .../lib/src/kotlin/kotlin_generator.dart | 78 +++++++- .../com/example/test_plugin/CoreTests.gen.kt | 185 ++++++++++++++++++ .../test_plugin/EventChannelTests.gen.kt | 151 ++++++++++++++ .../example/test_plugin/AllDatatypesTest.kt | 165 ++++++++++------ packages/pigeon/pubspec.yaml | 2 +- 9 files changed, 586 insertions(+), 63 deletions(-) diff --git a/packages/pigeon/CHANGELOG.md b/packages/pigeon/CHANGELOG.md index c0b1841a240..da10f9f6464 100644 --- a/packages/pigeon/CHANGELOG.md +++ b/packages/pigeon/CHANGELOG.md @@ -1,3 +1,7 @@ +## 25.2.0 + +* [kotlin] Adds equality methods to generated data classes. + ## 25.1.0 * [dart] Adds equality methods to generated data classes. diff --git a/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/EventChannelMessages.g.kt b/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/EventChannelMessages.g.kt index 0c0512df675..519c9e20a10 100644 --- a/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/EventChannelMessages.g.kt +++ b/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/EventChannelMessages.g.kt @@ -31,6 +31,18 @@ data class IntEvent(val data: Long) : PlatformEvent() { data, ) } + + override fun equals(other: Any?): Boolean { + if (other !is IntEvent) { + return false + } + if (this === other) { + return true + } + return data == other.data + } + + override fun hashCode(): Int = toList().hashCode() } /** Generated class from Pigeon that represents data sent in messages. */ @@ -47,6 +59,18 @@ data class StringEvent(val data: String) : PlatformEvent() { data, ) } + + override fun equals(other: Any?): Boolean { + if (other !is StringEvent) { + return false + } + if (this === other) { + return true + } + return data == other.data + } + + override fun hashCode(): Int = toList().hashCode() } private open class EventChannelMessagesPigeonCodec : StandardMessageCodec() { diff --git a/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/Messages.g.kt b/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/Messages.g.kt index c6b32713ebf..279f73141e8 100644 --- a/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/Messages.g.kt +++ b/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/Messages.g.kt @@ -46,6 +46,29 @@ class FlutterError( val details: Any? = null ) : Throwable() +private fun deepEqualsMessages(a: Any?, b: Any?): Boolean { + if (a is ByteArray && b is ByteArray) { + return a.contentEquals(b) + } + if (a is IntArray && b is IntArray) { + return a.contentEquals(b) + } + if (a is LongArray && b is LongArray) { + return a.contentEquals(b) + } + if (a is DoubleArray && b is DoubleArray) { + return a.contentEquals(b) + } + if (a is Array<*> && b is Array<*>) { + return a.size == b.size && a.indices.all { deepEqualsMessages(a[it], b[it]) } + } + if (a is Map<*, *> && b is Map<*, *>) { + return a.size == b.size && + a.keys.all { (b as Map).containsKey(it) && deepEqualsMessages(a[it], b[it]) } + } + return a == b +} + enum class Code(val raw: Int) { ONE(0), TWO(1); @@ -82,6 +105,21 @@ data class MessageData( data, ) } + + override fun equals(other: Any?): Boolean { + if (other !is MessageData) { + return false + } + if (this === other) { + return true + } + return name == other.name && + description == other.description && + code == other.code && + deepEqualsMessages(data, other.data) + } + + override fun hashCode(): Int = toList().hashCode() } private open class MessagesPigeonCodec : StandardMessageCodec() { diff --git a/packages/pigeon/lib/src/generator_tools.dart b/packages/pigeon/lib/src/generator_tools.dart index dd1b3d7af2d..94dc56c8f02 100644 --- a/packages/pigeon/lib/src/generator_tools.dart +++ b/packages/pigeon/lib/src/generator_tools.dart @@ -14,7 +14,7 @@ import 'ast.dart'; /// The current version of pigeon. /// /// This must match the version in pubspec.yaml. -const String pigeonVersion = '25.1.0'; +const String pigeonVersion = '25.2.0'; /// Read all the content from [stdin] to a String. String readStdin() { diff --git a/packages/pigeon/lib/src/kotlin/kotlin_generator.dart b/packages/pigeon/lib/src/kotlin/kotlin_generator.dart index 31f298382e1..1e439412407 100644 --- a/packages/pigeon/lib/src/kotlin/kotlin_generator.dart +++ b/packages/pigeon/lib/src/kotlin/kotlin_generator.dart @@ -291,9 +291,48 @@ class KotlinGenerator extends StructuredGenerator { classDefinition, dartPackageName: dartPackageName, ); + writeClassEquality( + generatorOptions, + root, + indent, + classDefinition, + dartPackageName: dartPackageName, + ); }); } + @override + void writeClassEquality( + InternalKotlinOptions generatorOptions, + Root root, + Indent indent, + Class classDefinition, { + required String dartPackageName, + }) { + indent.writeScoped('override fun equals(other: Any?): Boolean {', '}', () { + indent.writeScoped('if (other !is ${classDefinition.name}) {', '}', () { + indent.writeln('return false'); + }); + indent.writeScoped('if (this === other) {', '}', () { + indent.writeln('return true'); + }); + indent.write('return '); + indent.format(classDefinition.fields + .map((NamedType field) => field.type.baseName == 'List' || + field.type.baseName == 'Float64List' || + field.type.baseName == 'Int32List' || + field.type.baseName == 'Int64List' || + field.type.baseName == 'Uint8List' || + field.type.baseName == 'Map' + ? 'deepEquals${generatorOptions.fileSpecificClassNameComponent}(${field.name}, other.${field.name})' + : '${field.name} == other.${field.name}') + .join('\n&& ')); + }); + + indent.newln(); + indent.writeln('override fun hashCode(): Int = toList().hashCode()'); + } + void _writeDataClassSignature( Indent indent, Class classDefinition, { @@ -507,7 +546,7 @@ class KotlinGenerator extends StructuredGenerator { indent.newln(); if (root.containsEventChannel) { indent.writeln( - 'val ${generatorOptions.fileSpecificClassNameComponent}$_pigeonMethodChannelCodec = StandardMethodCodec(${generatorOptions.fileSpecificClassNameComponent}$_codecName());'); + 'val ${generatorOptions.fileSpecificClassNameComponent}$_pigeonMethodChannelCodec = StandardMethodCodec(${generatorOptions.fileSpecificClassNameComponent}$_codecName())'); indent.newln(); } } @@ -1219,6 +1258,36 @@ if (wrapped == null) { }); } + void _writeDeepEquals(InternalKotlinOptions generatorOptions, Indent indent) { + indent.format(''' +private fun deepEquals${generatorOptions.fileSpecificClassNameComponent}(a: Any?, b: Any?): Boolean { + if (a is ByteArray && b is ByteArray) { + return a.contentEquals(b) + } + if (a is IntArray && b is IntArray) { + return a.contentEquals(b) + } + if (a is LongArray && b is LongArray) { + return a.contentEquals(b) + } + if (a is DoubleArray && b is DoubleArray) { + return a.contentEquals(b) + } + if (a is Array<*> && b is Array<*>) { + return a.size == b.size && + a.indices.all{ deepEquals${generatorOptions.fileSpecificClassNameComponent}(a[it], b[it]) } + } + if (a is Map<*, *> && b is Map<*, *>) { + return a.size == b.size && a.keys.all { + (b as Map).containsKey(it) && + deepEquals${generatorOptions.fileSpecificClassNameComponent}(a[it], b[it]) + } + } + return a == b; +} + '''); + } + @override void writeGeneralUtilities( InternalKotlinOptions generatorOptions, @@ -1236,6 +1305,13 @@ if (wrapped == null) { if (generatorOptions.includeErrorClass) { _writeErrorClass(generatorOptions, indent); } + if (root.classes.isNotEmpty && + root.classes.any((Class dataClass) => dataClass.fields.any( + (NamedType field) => + field.type.baseName.startsWith('List') || + field.type.baseName.startsWith('Map')))) { + _writeDeepEquals(generatorOptions, indent); + } } static void _writeMethodDeclaration( diff --git a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/CoreTests.gen.kt b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/CoreTests.gen.kt index 467de3c5f29..6871e5fc61d 100644 --- a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/CoreTests.gen.kt +++ b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/CoreTests.gen.kt @@ -49,6 +49,29 @@ class FlutterError( val details: Any? = null ) : Throwable() +private fun deepEqualsCoreTests(a: Any?, b: Any?): Boolean { + if (a is ByteArray && b is ByteArray) { + return a.contentEquals(b) + } + if (a is IntArray && b is IntArray) { + return a.contentEquals(b) + } + if (a is LongArray && b is LongArray) { + return a.contentEquals(b) + } + if (a is DoubleArray && b is DoubleArray) { + return a.contentEquals(b) + } + if (a is Array<*> && b is Array<*>) { + return a.size == b.size && a.indices.all { deepEqualsCoreTests(a[it], b[it]) } + } + if (a is Map<*, *> && b is Map<*, *>) { + return a.size == b.size && + a.keys.all { (b as Map).containsKey(it) && deepEqualsCoreTests(a[it], b[it]) } + } + return a == b +} + enum class AnEnum(val raw: Int) { ONE(0), TWO(1), @@ -87,6 +110,18 @@ data class UnusedClass(val aField: Any? = null) { aField, ) } + + override fun equals(other: Any?): Boolean { + if (other !is UnusedClass) { + return false + } + if (this === other) { + return true + } + return aField == other.aField + } + + override fun hashCode(): Int = toList().hashCode() } /** @@ -218,6 +253,45 @@ data class AllTypes( mapMap, ) } + + override fun equals(other: Any?): Boolean { + if (other !is AllTypes) { + return false + } + if (this === other) { + return true + } + return aBool == other.aBool && + anInt == other.anInt && + anInt64 == other.anInt64 && + aDouble == other.aDouble && + deepEqualsCoreTests(aByteArray, other.aByteArray) && + deepEqualsCoreTests(a4ByteArray, other.a4ByteArray) && + deepEqualsCoreTests(a8ByteArray, other.a8ByteArray) && + deepEqualsCoreTests(aFloatArray, other.aFloatArray) && + anEnum == other.anEnum && + anotherEnum == other.anotherEnum && + aString == other.aString && + anObject == other.anObject && + deepEqualsCoreTests(list, other.list) && + deepEqualsCoreTests(stringList, other.stringList) && + deepEqualsCoreTests(intList, other.intList) && + deepEqualsCoreTests(doubleList, other.doubleList) && + deepEqualsCoreTests(boolList, other.boolList) && + deepEqualsCoreTests(enumList, other.enumList) && + deepEqualsCoreTests(objectList, other.objectList) && + deepEqualsCoreTests(listList, other.listList) && + deepEqualsCoreTests(mapList, other.mapList) && + deepEqualsCoreTests(map, other.map) && + deepEqualsCoreTests(stringMap, other.stringMap) && + deepEqualsCoreTests(intMap, other.intMap) && + deepEqualsCoreTests(enumMap, other.enumMap) && + deepEqualsCoreTests(objectMap, other.objectMap) && + deepEqualsCoreTests(listMap, other.listMap) && + deepEqualsCoreTests(mapMap, other.mapMap) + } + + override fun hashCode(): Int = toList().hashCode() } /** @@ -361,6 +435,48 @@ data class AllNullableTypes( recursiveClassMap, ) } + + override fun equals(other: Any?): Boolean { + if (other !is AllNullableTypes) { + return false + } + if (this === other) { + return true + } + return aNullableBool == other.aNullableBool && + aNullableInt == other.aNullableInt && + aNullableInt64 == other.aNullableInt64 && + aNullableDouble == other.aNullableDouble && + deepEqualsCoreTests(aNullableByteArray, other.aNullableByteArray) && + deepEqualsCoreTests(aNullable4ByteArray, other.aNullable4ByteArray) && + deepEqualsCoreTests(aNullable8ByteArray, other.aNullable8ByteArray) && + deepEqualsCoreTests(aNullableFloatArray, other.aNullableFloatArray) && + aNullableEnum == other.aNullableEnum && + anotherNullableEnum == other.anotherNullableEnum && + aNullableString == other.aNullableString && + aNullableObject == other.aNullableObject && + allNullableTypes == other.allNullableTypes && + deepEqualsCoreTests(list, other.list) && + deepEqualsCoreTests(stringList, other.stringList) && + deepEqualsCoreTests(intList, other.intList) && + deepEqualsCoreTests(doubleList, other.doubleList) && + deepEqualsCoreTests(boolList, other.boolList) && + deepEqualsCoreTests(enumList, other.enumList) && + deepEqualsCoreTests(objectList, other.objectList) && + deepEqualsCoreTests(listList, other.listList) && + deepEqualsCoreTests(mapList, other.mapList) && + deepEqualsCoreTests(recursiveClassList, other.recursiveClassList) && + deepEqualsCoreTests(map, other.map) && + deepEqualsCoreTests(stringMap, other.stringMap) && + deepEqualsCoreTests(intMap, other.intMap) && + deepEqualsCoreTests(enumMap, other.enumMap) && + deepEqualsCoreTests(objectMap, other.objectMap) && + deepEqualsCoreTests(listMap, other.listMap) && + deepEqualsCoreTests(mapMap, other.mapMap) && + deepEqualsCoreTests(recursiveClassMap, other.recursiveClassMap) + } + + override fun hashCode(): Int = toList().hashCode() } /** @@ -493,6 +609,45 @@ data class AllNullableTypesWithoutRecursion( mapMap, ) } + + override fun equals(other: Any?): Boolean { + if (other !is AllNullableTypesWithoutRecursion) { + return false + } + if (this === other) { + return true + } + return aNullableBool == other.aNullableBool && + aNullableInt == other.aNullableInt && + aNullableInt64 == other.aNullableInt64 && + aNullableDouble == other.aNullableDouble && + deepEqualsCoreTests(aNullableByteArray, other.aNullableByteArray) && + deepEqualsCoreTests(aNullable4ByteArray, other.aNullable4ByteArray) && + deepEqualsCoreTests(aNullable8ByteArray, other.aNullable8ByteArray) && + deepEqualsCoreTests(aNullableFloatArray, other.aNullableFloatArray) && + aNullableEnum == other.aNullableEnum && + anotherNullableEnum == other.anotherNullableEnum && + aNullableString == other.aNullableString && + aNullableObject == other.aNullableObject && + deepEqualsCoreTests(list, other.list) && + deepEqualsCoreTests(stringList, other.stringList) && + deepEqualsCoreTests(intList, other.intList) && + deepEqualsCoreTests(doubleList, other.doubleList) && + deepEqualsCoreTests(boolList, other.boolList) && + deepEqualsCoreTests(enumList, other.enumList) && + deepEqualsCoreTests(objectList, other.objectList) && + deepEqualsCoreTests(listList, other.listList) && + deepEqualsCoreTests(mapList, other.mapList) && + deepEqualsCoreTests(map, other.map) && + deepEqualsCoreTests(stringMap, other.stringMap) && + deepEqualsCoreTests(intMap, other.intMap) && + deepEqualsCoreTests(enumMap, other.enumMap) && + deepEqualsCoreTests(objectMap, other.objectMap) && + deepEqualsCoreTests(listMap, other.listMap) && + deepEqualsCoreTests(mapMap, other.mapMap) + } + + override fun hashCode(): Int = toList().hashCode() } /** @@ -544,6 +699,24 @@ data class AllClassesWrapper( nullableClassMap, ) } + + override fun equals(other: Any?): Boolean { + if (other !is AllClassesWrapper) { + return false + } + if (this === other) { + return true + } + return allNullableTypes == other.allNullableTypes && + allNullableTypesWithoutRecursion == other.allNullableTypesWithoutRecursion && + allTypes == other.allTypes && + deepEqualsCoreTests(classList, other.classList) && + deepEqualsCoreTests(nullableClassList, other.nullableClassList) && + deepEqualsCoreTests(classMap, other.classMap) && + deepEqualsCoreTests(nullableClassMap, other.nullableClassMap) + } + + override fun hashCode(): Int = toList().hashCode() } /** @@ -564,6 +737,18 @@ data class TestMessage(val testList: List? = null) { testList, ) } + + override fun equals(other: Any?): Boolean { + if (other !is TestMessage) { + return false + } + if (this === other) { + return true + } + return deepEqualsCoreTests(testList, other.testList) + } + + override fun hashCode(): Int = toList().hashCode() } private open class CoreTestsPigeonCodec : StandardMessageCodec() { diff --git a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/EventChannelTests.gen.kt b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/EventChannelTests.gen.kt index 1f24c42aed4..6d39a54f327 100644 --- a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/EventChannelTests.gen.kt +++ b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/EventChannelTests.gen.kt @@ -28,6 +28,31 @@ class EventChannelTestsError( val details: Any? = null ) : Throwable() +private fun deepEqualsEventChannelTests(a: Any?, b: Any?): Boolean { + if (a is ByteArray && b is ByteArray) { + return a.contentEquals(b) + } + if (a is IntArray && b is IntArray) { + return a.contentEquals(b) + } + if (a is LongArray && b is LongArray) { + return a.contentEquals(b) + } + if (a is DoubleArray && b is DoubleArray) { + return a.contentEquals(b) + } + if (a is Array<*> && b is Array<*>) { + return a.size == b.size && a.indices.all { deepEqualsEventChannelTests(a[it], b[it]) } + } + if (a is Map<*, *> && b is Map<*, *>) { + return a.size == b.size && + a.keys.all { + (b as Map).containsKey(it) && deepEqualsEventChannelTests(a[it], b[it]) + } + } + return a == b +} + enum class EventEnum(val raw: Int) { ONE(0), TWO(1), @@ -193,6 +218,48 @@ data class EventAllNullableTypes( recursiveClassMap, ) } + + override fun equals(other: Any?): Boolean { + if (other !is EventAllNullableTypes) { + return false + } + if (this === other) { + return true + } + return aNullableBool == other.aNullableBool && + aNullableInt == other.aNullableInt && + aNullableInt64 == other.aNullableInt64 && + aNullableDouble == other.aNullableDouble && + deepEqualsEventChannelTests(aNullableByteArray, other.aNullableByteArray) && + deepEqualsEventChannelTests(aNullable4ByteArray, other.aNullable4ByteArray) && + deepEqualsEventChannelTests(aNullable8ByteArray, other.aNullable8ByteArray) && + deepEqualsEventChannelTests(aNullableFloatArray, other.aNullableFloatArray) && + aNullableEnum == other.aNullableEnum && + anotherNullableEnum == other.anotherNullableEnum && + aNullableString == other.aNullableString && + aNullableObject == other.aNullableObject && + allNullableTypes == other.allNullableTypes && + deepEqualsEventChannelTests(list, other.list) && + deepEqualsEventChannelTests(stringList, other.stringList) && + deepEqualsEventChannelTests(intList, other.intList) && + deepEqualsEventChannelTests(doubleList, other.doubleList) && + deepEqualsEventChannelTests(boolList, other.boolList) && + deepEqualsEventChannelTests(enumList, other.enumList) && + deepEqualsEventChannelTests(objectList, other.objectList) && + deepEqualsEventChannelTests(listList, other.listList) && + deepEqualsEventChannelTests(mapList, other.mapList) && + deepEqualsEventChannelTests(recursiveClassList, other.recursiveClassList) && + deepEqualsEventChannelTests(map, other.map) && + deepEqualsEventChannelTests(stringMap, other.stringMap) && + deepEqualsEventChannelTests(intMap, other.intMap) && + deepEqualsEventChannelTests(enumMap, other.enumMap) && + deepEqualsEventChannelTests(objectMap, other.objectMap) && + deepEqualsEventChannelTests(listMap, other.listMap) && + deepEqualsEventChannelTests(mapMap, other.mapMap) && + deepEqualsEventChannelTests(recursiveClassMap, other.recursiveClassMap) + } + + override fun hashCode(): Int = toList().hashCode() } /** @@ -214,6 +281,18 @@ data class IntEvent(val value: Long) : PlatformEvent() { value, ) } + + override fun equals(other: Any?): Boolean { + if (other !is IntEvent) { + return false + } + if (this === other) { + return true + } + return value == other.value + } + + override fun hashCode(): Int = toList().hashCode() } /** Generated class from Pigeon that represents data sent in messages. */ @@ -230,6 +309,18 @@ data class StringEvent(val value: String) : PlatformEvent() { value, ) } + + override fun equals(other: Any?): Boolean { + if (other !is StringEvent) { + return false + } + if (this === other) { + return true + } + return value == other.value + } + + override fun hashCode(): Int = toList().hashCode() } /** Generated class from Pigeon that represents data sent in messages. */ @@ -246,6 +337,18 @@ data class BoolEvent(val value: Boolean) : PlatformEvent() { value, ) } + + override fun equals(other: Any?): Boolean { + if (other !is BoolEvent) { + return false + } + if (this === other) { + return true + } + return value == other.value + } + + override fun hashCode(): Int = toList().hashCode() } /** Generated class from Pigeon that represents data sent in messages. */ @@ -262,6 +365,18 @@ data class DoubleEvent(val value: Double) : PlatformEvent() { value, ) } + + override fun equals(other: Any?): Boolean { + if (other !is DoubleEvent) { + return false + } + if (this === other) { + return true + } + return value == other.value + } + + override fun hashCode(): Int = toList().hashCode() } /** Generated class from Pigeon that represents data sent in messages. */ @@ -278,6 +393,18 @@ data class ObjectsEvent(val value: Any) : PlatformEvent() { value, ) } + + override fun equals(other: Any?): Boolean { + if (other !is ObjectsEvent) { + return false + } + if (this === other) { + return true + } + return value == other.value + } + + override fun hashCode(): Int = toList().hashCode() } /** Generated class from Pigeon that represents data sent in messages. */ @@ -294,6 +421,18 @@ data class EnumEvent(val value: EventEnum) : PlatformEvent() { value, ) } + + override fun equals(other: Any?): Boolean { + if (other !is EnumEvent) { + return false + } + if (this === other) { + return true + } + return value == other.value + } + + override fun hashCode(): Int = toList().hashCode() } /** Generated class from Pigeon that represents data sent in messages. */ @@ -310,6 +449,18 @@ data class ClassEvent(val value: EventAllNullableTypes) : PlatformEvent() { value, ) } + + override fun equals(other: Any?): Boolean { + if (other !is ClassEvent) { + return false + } + if (this === other) { + return true + } + return value == other.value + } + + override fun hashCode(): Int = toList().hashCode() } private open class EventChannelTestsPigeonCodec : StandardMessageCodec() { diff --git a/packages/pigeon/platform_tests/test_plugin/android/src/test/kotlin/com/example/test_plugin/AllDatatypesTest.kt b/packages/pigeon/platform_tests/test_plugin/android/src/test/kotlin/com/example/test_plugin/AllDatatypesTest.kt index aa42535f718..a630b270ce3 100644 --- a/packages/pigeon/platform_tests/test_plugin/android/src/test/kotlin/com/example/test_plugin/AllDatatypesTest.kt +++ b/packages/pigeon/platform_tests/test_plugin/android/src/test/kotlin/com/example/test_plugin/AllDatatypesTest.kt @@ -10,70 +10,13 @@ import io.mockk.mockk import java.nio.ByteBuffer import java.util.ArrayList import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotEquals import org.junit.Assert.assertNotNull import org.junit.Assert.assertTrue import org.junit.Test internal class AllDatatypesTest { - fun compareAllTypes(firstTypes: AllTypes?, secondTypes: AllTypes?) { - assertEquals(firstTypes == null, secondTypes == null) - if (firstTypes == null || secondTypes == null) { - return - } - assertEquals(firstTypes.aBool, secondTypes.aBool) - assertEquals(firstTypes.anInt, secondTypes.anInt) - assertEquals(firstTypes.anInt64, secondTypes.anInt64) - assertEquals(firstTypes.aDouble, secondTypes.aDouble, 0.0) - assertEquals(firstTypes.aString, secondTypes.aString) - assertTrue(firstTypes.aByteArray.contentEquals(secondTypes.aByteArray)) - assertTrue(firstTypes.a4ByteArray.contentEquals(secondTypes.a4ByteArray)) - assertTrue(firstTypes.a8ByteArray.contentEquals(secondTypes.a8ByteArray)) - assertTrue(firstTypes.aFloatArray.contentEquals(secondTypes.aFloatArray)) - assertEquals(firstTypes.anEnum, secondTypes.anEnum) - assertEquals(firstTypes.anotherEnum, secondTypes.anotherEnum) - assertEquals(firstTypes.anObject, secondTypes.anObject) - assertEquals(firstTypes.list, secondTypes.list) - assertEquals(firstTypes.boolList, secondTypes.boolList) - assertEquals(firstTypes.doubleList, secondTypes.doubleList) - assertEquals(firstTypes.intList, secondTypes.intList) - assertEquals(firstTypes.stringList, secondTypes.stringList) - assertEquals(firstTypes.map, secondTypes.map) - } - - private fun compareAllNullableTypes( - firstTypes: AllNullableTypes?, - secondTypes: AllNullableTypes? - ) { - assertEquals(firstTypes == null, secondTypes == null) - if (firstTypes == null || secondTypes == null) { - return - } - assertEquals(firstTypes.aNullableBool, secondTypes.aNullableBool) - assertEquals(firstTypes.aNullableInt, secondTypes.aNullableInt) - assertEquals(firstTypes.aNullableDouble, secondTypes.aNullableDouble) - assertEquals(firstTypes.aNullableString, secondTypes.aNullableString) - assertTrue(firstTypes.aNullableByteArray.contentEquals(secondTypes.aNullableByteArray)) - assertTrue(firstTypes.aNullable4ByteArray.contentEquals(secondTypes.aNullable4ByteArray)) - assertTrue(firstTypes.aNullable8ByteArray.contentEquals(secondTypes.aNullable8ByteArray)) - assertTrue(firstTypes.aNullableFloatArray.contentEquals(secondTypes.aNullableFloatArray)) - assertEquals(firstTypes.aNullableObject, secondTypes.aNullableObject) - assertEquals(firstTypes.aNullableEnum, secondTypes.aNullableEnum) - assertEquals(firstTypes.anotherNullableEnum, secondTypes.anotherNullableEnum) - assertEquals(firstTypes.list, secondTypes.list) - assertEquals(firstTypes.boolList, secondTypes.boolList) - assertEquals(firstTypes.doubleList, secondTypes.doubleList) - assertEquals(firstTypes.intList, secondTypes.intList) - assertEquals(firstTypes.stringList, secondTypes.stringList) - assertEquals(firstTypes.listList, secondTypes.listList) - assertEquals(firstTypes.mapList, secondTypes.mapList) - assertEquals(firstTypes.map, secondTypes.map) - assertEquals(firstTypes.stringMap, secondTypes.stringMap) - assertEquals(firstTypes.intMap, secondTypes.intMap) - assertEquals(firstTypes.listMap, secondTypes.listMap) - assertEquals(firstTypes.mapMap, secondTypes.mapMap) - } - @Test fun testNullValues() { val everything = AllNullableTypes() @@ -95,7 +38,7 @@ internal class AllDatatypesTest { var didCall = false api.echoAllNullableTypes(everything) { result -> didCall = true - val output = (result.getOrNull())?.let { compareAllNullableTypes(it, everything) } + val output = (result.getOrNull())?.let { it == everything } assertNotNull(output) } @@ -148,9 +91,111 @@ internal class AllDatatypesTest { var didCall = false api.echoAllNullableTypes(everything) { didCall = true - compareAllNullableTypes(everything, it.getOrNull()) + assertTrue(everything == it.getOrNull()) } assertTrue(didCall) } + + private val correctList = listOf("a", 2, "three") + private val matchingList = correctList.toMutableList() + private val differentList = listOf("a", 2, "three", 4.0) + + private val correctMap = mapOf("a" to 1, "b" to 2, "c" to "three") + private val matchingMap = correctMap.toMap() + private val differentKeyMap = mapOf("a" to 1, "b" to 2, "d" to "three") + private val differentValueMap = mapOf("a" to 1, "b" to 2, "c" to "five") + + private val correctListInMap = mapOf("a" to 1, "b" to 2, "c" to correctList) + private val matchingListInMap = mapOf("a" to 1, "b" to 2, "c" to matchingList) + private val differentListInMap = mapOf("a" to 1, "b" to 2, "c" to differentList) + + private val correctMapInList = listOf("a", 2, correctMap) + private val matchingMapInList = listOf("a", 2, matchingMap) + private val differentKeyMapInList = listOf("a", 2, differentKeyMap) + private val differentValueMapInList = listOf("a", 2, differentValueMap) + + @Test + fun `equality method correctly checks deep equality`() { + val generic = AllNullableTypes(list = correctList, map = correctMap) + val identical = generic.copy() + assertEquals(generic, identical) + } + + @Test + fun `equality method correctly identifies non-matching classes`() { + val generic = AllNullableTypes(list = correctList, map = correctMap) + val allNull = AllNullableTypes() + assertNotEquals(allNull, generic) + } + + @Test + fun `equality method correctly identifies non-matching lists in classes`() { + val withList = AllNullableTypes(list = correctList) + val withDifferentList = AllNullableTypes(list = differentList) + assertNotEquals(withList, withDifferentList) + } + + @Test + fun `equality method correctly identifies matching -but unique- lists in classes`() { + val withList = AllNullableTypes(list = correctList) + val withDifferentList = AllNullableTypes(list = matchingList) + assertEquals(withList, withDifferentList) + } + + @Test + fun `equality method correctly identifies non-matching keys in maps in classes`() { + val withMap = AllNullableTypes(map = correctMap) + val withDifferentMap = AllNullableTypes(map = differentKeyMap) + assertNotEquals(withMap, withDifferentMap) + } + + @Test + fun `equality method correctly identifies non-matching values in maps in classes`() { + val withMap = AllNullableTypes(map = correctMap) + val withDifferentMap = AllNullableTypes(map = differentValueMap) + assertNotEquals(withMap, withDifferentMap) + } + + @Test + fun `equality method correctly identifies matching -but unique- maps in classes`() { + val withMap = AllNullableTypes(map = correctMap) + val withDifferentMap = AllNullableTypes(map = matchingMap) + assertEquals(withMap, withDifferentMap) + } + + @Test + fun `equality method correctly identifies non-matching lists nested in maps in classes`() { + val withListInMap = AllNullableTypes(map = correctListInMap) + val withDifferentListInMap = AllNullableTypes(map = differentListInMap) + assertNotEquals(withListInMap, withDifferentListInMap) + } + + @Test + fun `equality method correctly identifies matching -but unique- lists nested in maps in classes`() { + val withListInMap = AllNullableTypes(map = correctListInMap) + val withDifferentListInMap = AllNullableTypes(map = matchingListInMap) + assertEquals(withListInMap, withDifferentListInMap) + } + + @Test + fun `equality method correctly identifies non-matching keys in maps nested in lists in classes`() { + val withMapInList = AllNullableTypes(list = correctMapInList) + val withDifferentMapInList = AllNullableTypes(list = differentKeyMapInList) + assertNotEquals(withMapInList, withDifferentMapInList) + } + + @Test + fun `equality method correctly identifies non-matching values in maps nested in lists in classes`() { + val withMapInList = AllNullableTypes(list = correctMapInList) + val withDifferentMapInList = AllNullableTypes(list = differentValueMapInList) + assertNotEquals(withMapInList, withDifferentMapInList) + } + + @Test + fun `equality method correctly identifies matching -but unique- maps nested in lists in classes`() { + val withMapInList = AllNullableTypes(list = correctMapInList) + val withDifferentMapInList = AllNullableTypes(list = matchingMapInList) + assertEquals(withMapInList, withDifferentMapInList) + } } diff --git a/packages/pigeon/pubspec.yaml b/packages/pigeon/pubspec.yaml index a1e5e436c32..3459232c984 100644 --- a/packages/pigeon/pubspec.yaml +++ b/packages/pigeon/pubspec.yaml @@ -2,7 +2,7 @@ name: pigeon description: Code generator tool to make communication between Flutter and the host platform type-safe and easier. repository: https://github.com/flutter/packages/tree/main/packages/pigeon issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+pigeon%22 -version: 25.1.0 # This must match the version in lib/src/generator_tools.dart +version: 25.2.0 # This must match the version in lib/src/generator_tools.dart environment: sdk: ^3.4.0 From 8613ea98385a8c3d191db17913d3f699a2d99e03 Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Thu, 20 Mar 2025 15:52:10 -0700 Subject: [PATCH 2/6] add more scenarios to write deepEquals --- packages/pigeon/lib/src/kotlin/kotlin_generator.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/pigeon/lib/src/kotlin/kotlin_generator.dart b/packages/pigeon/lib/src/kotlin/kotlin_generator.dart index 1e439412407..6ad735c94c9 100644 --- a/packages/pigeon/lib/src/kotlin/kotlin_generator.dart +++ b/packages/pigeon/lib/src/kotlin/kotlin_generator.dart @@ -1308,8 +1308,9 @@ private fun deepEquals${generatorOptions.fileSpecificClassNameComponent}(a: Any? if (root.classes.isNotEmpty && root.classes.any((Class dataClass) => dataClass.fields.any( (NamedType field) => - field.type.baseName.startsWith('List') || - field.type.baseName.startsWith('Map')))) { + _kotlinTypeForBuiltinDartType(field.type) != null && + (field.type.baseName.contains('List') || + field.type.baseName.startsWith('Map'))))) { _writeDeepEquals(generatorOptions, indent); } } From 2dabedb452a38d796ccf5b1129b25f92108bc80d Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Thu, 20 Mar 2025 16:32:41 -0700 Subject: [PATCH 3/6] fix logic on dart deep equals utility --- packages/pigeon/lib/src/dart/dart_generator.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/pigeon/lib/src/dart/dart_generator.dart b/packages/pigeon/lib/src/dart/dart_generator.dart index 1ea9b650b38..b73027dfae1 100644 --- a/packages/pigeon/lib/src/dart/dart_generator.dart +++ b/packages/pigeon/lib/src/dart/dart_generator.dart @@ -1091,8 +1091,10 @@ final BinaryMessenger? ${varNamePrefix}binaryMessenger; if (root.classes.isNotEmpty && root.classes.any((Class dataClass) => dataClass.fields.any( (NamedType field) => - field.type.baseName.startsWith('List') || - field.type.baseName.startsWith('Map')))) { + !field.type.isClass && + !field.type.isEnum && + (field.type.baseName.contains('List') || + field.type.baseName.startsWith('Map'))))) { _writeDeepEquals(indent); } } From 48fa3493f9e8dc277310407f1c3830ec52f4deb2 Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Mon, 24 Mar 2025 15:24:37 -0700 Subject: [PATCH 4/6] update Internal options naming and extension --- .../pigeon/lib/src/cpp/cpp_generator.dart | 2 +- .../pigeon/lib/src/dart/dart_generator.dart | 2 +- packages/pigeon/lib/src/generator.dart | 57 ++++++++++--------- .../lib/src/gobject/gobject_generator.dart | 2 +- .../pigeon/lib/src/java/java_generator.dart | 2 +- .../lib/src/kotlin/kotlin_generator.dart | 2 +- .../pigeon/lib/src/objc/objc_generator.dart | 2 +- .../pigeon/lib/src/swift/swift_generator.dart | 2 +- 8 files changed, 37 insertions(+), 34 deletions(-) diff --git a/packages/pigeon/lib/src/cpp/cpp_generator.dart b/packages/pigeon/lib/src/cpp/cpp_generator.dart index 3db417af287..cad25aa2a4c 100644 --- a/packages/pigeon/lib/src/cpp/cpp_generator.dart +++ b/packages/pigeon/lib/src/cpp/cpp_generator.dart @@ -98,7 +98,7 @@ class CppOptions { /// Options that control how C++ code will be generated. /// /// For internal use only. -class InternalCppOptions { +class InternalCppOptions extends PigeonInternalOptions { /// Creates a [InternalCppOptions] object. const InternalCppOptions({ required this.headerIncludePath, diff --git a/packages/pigeon/lib/src/dart/dart_generator.dart b/packages/pigeon/lib/src/dart/dart_generator.dart index b73027dfae1..9fa7260ce03 100644 --- a/packages/pigeon/lib/src/dart/dart_generator.dart +++ b/packages/pigeon/lib/src/dart/dart_generator.dart @@ -87,7 +87,7 @@ class DartOptions { } /// Options that control how Dart code will be generated. -class InternalDartOptions { +class InternalDartOptions extends PigeonInternalOptions { /// Constructor for InternalDartOptions. const InternalDartOptions({ this.copyrightHeader, diff --git a/packages/pigeon/lib/src/generator.dart b/packages/pigeon/lib/src/generator.dart index 66961c4f3c5..fc0b8a1c50c 100644 --- a/packages/pigeon/lib/src/generator.dart +++ b/packages/pigeon/lib/src/generator.dart @@ -6,18 +6,21 @@ import 'ast.dart'; import 'generator_tools.dart'; /// The internal options used by the generator. -abstract class InternalOptions {} +abstract class PigeonInternalOptions { + /// Constructor. + const PigeonInternalOptions(); +} /// An abstract base class of generators. /// /// This provides the structure that is common across generators for different languages. -abstract class Generator { +abstract class Generator { /// Constructor. const Generator(); /// Generates files for specified language with specified [generatorOptions] void generate( - InternalOptions generatorOptions, + PigeonInternalOptions generatorOptions, Root root, StringSink sink, { required String dartPackageName, @@ -25,14 +28,14 @@ abstract class Generator { } /// An abstract base class that enforces code generation across platforms. -abstract class StructuredGenerator - extends Generator { +abstract class StructuredGenerator + extends Generator { /// Constructor. const StructuredGenerator(); @override void generate( - InternalOptions generatorOptions, + PigeonInternalOptions generatorOptions, Root root, StringSink sink, { required String dartPackageName, @@ -123,7 +126,7 @@ abstract class StructuredGenerator /// Adds specified headers to [indent]. void writeFilePrologue( - InternalOptions generatorOptions, + PigeonInternalOptions generatorOptions, Root root, Indent indent, { required String dartPackageName, @@ -131,7 +134,7 @@ abstract class StructuredGenerator /// Writes specified imports to [indent]. void writeFileImports( - InternalOptions generatorOptions, + PigeonInternalOptions generatorOptions, Root root, Indent indent, { required String dartPackageName, @@ -141,7 +144,7 @@ abstract class StructuredGenerator /// /// This method is not required, and does not need to be overridden. void writeOpenNamespace( - InternalOptions generatorOptions, + PigeonInternalOptions generatorOptions, Root root, Indent indent, { required String dartPackageName, @@ -151,7 +154,7 @@ abstract class StructuredGenerator /// /// This method is not required, and does not need to be overridden. void writeCloseNamespace( - InternalOptions generatorOptions, + PigeonInternalOptions generatorOptions, Root root, Indent indent, { required String dartPackageName, @@ -161,7 +164,7 @@ abstract class StructuredGenerator /// /// This method is not required, and does not need to be overridden. void writeGeneralUtilities( - InternalOptions generatorOptions, + PigeonInternalOptions generatorOptions, Root root, Indent indent, { required String dartPackageName, @@ -171,7 +174,7 @@ abstract class StructuredGenerator /// /// Can be overridden to add extra code before/after enums. void writeEnums( - InternalOptions generatorOptions, + PigeonInternalOptions generatorOptions, Root root, Indent indent, { required String dartPackageName, @@ -189,7 +192,7 @@ abstract class StructuredGenerator /// Writes a single Enum to [indent]. This is needed in most generators. void writeEnum( - InternalOptions generatorOptions, + PigeonInternalOptions generatorOptions, Root root, Indent indent, Enum anEnum, { @@ -200,7 +203,7 @@ abstract class StructuredGenerator /// /// Can be overridden to add extra code before/after apis. void writeDataClasses( - InternalOptions generatorOptions, + PigeonInternalOptions generatorOptions, Root root, Indent indent, { required String dartPackageName, @@ -218,7 +221,7 @@ abstract class StructuredGenerator /// Writes the custom codec to [indent]. void writeGeneralCodec( - InternalOptions generatorOptions, + PigeonInternalOptions generatorOptions, Root root, Indent indent, { required String dartPackageName, @@ -226,7 +229,7 @@ abstract class StructuredGenerator /// Writes a single data class to [indent]. void writeDataClass( - InternalOptions generatorOptions, + PigeonInternalOptions generatorOptions, Root root, Indent indent, Class classDefinition, { @@ -235,7 +238,7 @@ abstract class StructuredGenerator /// Writes a single class encode method to [indent]. void writeClassEncode( - InternalOptions generatorOptions, + PigeonInternalOptions generatorOptions, Root root, Indent indent, Class classDefinition, { @@ -244,7 +247,7 @@ abstract class StructuredGenerator /// Writes a single class decode method to [indent]. void writeClassDecode( - InternalOptions generatorOptions, + PigeonInternalOptions generatorOptions, Root root, Indent indent, Class classDefinition, { @@ -253,7 +256,7 @@ abstract class StructuredGenerator /// Writes a single class decode method to [indent]. void writeClassEquality( - InternalOptions generatorOptions, + PigeonInternalOptions generatorOptions, Root root, Indent indent, Class classDefinition, { @@ -264,7 +267,7 @@ abstract class StructuredGenerator /// /// Can be overridden to add extra code before/after classes. void writeApis( - InternalOptions generatorOptions, + PigeonInternalOptions generatorOptions, Root root, Indent indent, { required String dartPackageName, @@ -309,7 +312,7 @@ abstract class StructuredGenerator /// Writes a single Flutter Api to [indent]. void writeFlutterApi( - InternalOptions generatorOptions, + PigeonInternalOptions generatorOptions, Root root, Indent indent, AstFlutterApi api, { @@ -318,7 +321,7 @@ abstract class StructuredGenerator /// Writes a single Host Api to [indent]. void writeHostApi( - InternalOptions generatorOptions, + PigeonInternalOptions generatorOptions, Root root, Indent indent, AstHostApi api, { @@ -327,7 +330,7 @@ abstract class StructuredGenerator /// Writes the implementation of an `InstanceManager` to [indent]. void writeInstanceManager( - InternalOptions generatorOptions, + PigeonInternalOptions generatorOptions, Root root, Indent indent, { required String dartPackageName, @@ -336,7 +339,7 @@ abstract class StructuredGenerator /// Writes the implementation of the API for the `InstanceManager` to /// [indent]. void writeInstanceManagerApi( - InternalOptions generatorOptions, + PigeonInternalOptions generatorOptions, Root root, Indent indent, { required String dartPackageName, @@ -353,14 +356,14 @@ abstract class StructuredGenerator /// needs to create its own codec (it has methods/fields/constructor that use /// a data class) it should extend this codec and not `StandardMessageCodec`. void writeProxyApiBaseCodec( - InternalOptions generatorOptions, + PigeonInternalOptions generatorOptions, Root root, Indent indent, ) {} /// Writes a single Proxy Api to [indent]. void writeProxyApi( - InternalOptions generatorOptions, + PigeonInternalOptions generatorOptions, Root root, Indent indent, AstProxyApi api, { @@ -369,7 +372,7 @@ abstract class StructuredGenerator /// Writes a single event channel Api to [indent]. void writeEventChannelApi( - InternalOptions generatorOptions, + PigeonInternalOptions generatorOptions, Root root, Indent indent, AstEventChannelApi api, { diff --git a/packages/pigeon/lib/src/gobject/gobject_generator.dart b/packages/pigeon/lib/src/gobject/gobject_generator.dart index 3469c981540..9cb17968c72 100644 --- a/packages/pigeon/lib/src/gobject/gobject_generator.dart +++ b/packages/pigeon/lib/src/gobject/gobject_generator.dart @@ -74,7 +74,7 @@ class GObjectOptions { } /// Options that control how GObject code will be generated. -class InternalGObjectOptions { +class InternalGObjectOptions extends PigeonInternalOptions { /// Creates a [InternalGObjectOptions] object const InternalGObjectOptions({ required this.headerIncludePath, diff --git a/packages/pigeon/lib/src/java/java_generator.dart b/packages/pigeon/lib/src/java/java_generator.dart index ec9c37aeb2e..26b346da62a 100644 --- a/packages/pigeon/lib/src/java/java_generator.dart +++ b/packages/pigeon/lib/src/java/java_generator.dart @@ -90,7 +90,7 @@ class JavaOptions { } /// Options that control how Java code will be generated. -class InternalJavaOptions { +class InternalJavaOptions extends PigeonInternalOptions { /// Creates a [InternalJavaOptions] object const InternalJavaOptions({ required this.javaOut, diff --git a/packages/pigeon/lib/src/kotlin/kotlin_generator.dart b/packages/pigeon/lib/src/kotlin/kotlin_generator.dart index 6ad735c94c9..df9fe78b719 100644 --- a/packages/pigeon/lib/src/kotlin/kotlin_generator.dart +++ b/packages/pigeon/lib/src/kotlin/kotlin_generator.dart @@ -99,7 +99,7 @@ class KotlinOptions { } /// -class InternalKotlinOptions { +class InternalKotlinOptions extends PigeonInternalOptions { /// Creates a [InternalKotlinOptions] object const InternalKotlinOptions({ this.package, diff --git a/packages/pigeon/lib/src/objc/objc_generator.dart b/packages/pigeon/lib/src/objc/objc_generator.dart index 85b92bd4c38..0a2e91384bb 100644 --- a/packages/pigeon/lib/src/objc/objc_generator.dart +++ b/packages/pigeon/lib/src/objc/objc_generator.dart @@ -93,7 +93,7 @@ class ObjcOptions { } /// Options that control how Objective-C code will be generated. -class InternalObjcOptions { +class InternalObjcOptions extends PigeonInternalOptions { /// Parametric constructor for InternalObjcOptions. const InternalObjcOptions({ required this.headerIncludePath, diff --git a/packages/pigeon/lib/src/swift/swift_generator.dart b/packages/pigeon/lib/src/swift/swift_generator.dart index 88aa792c5e5..c9d2cdcf569 100644 --- a/packages/pigeon/lib/src/swift/swift_generator.dart +++ b/packages/pigeon/lib/src/swift/swift_generator.dart @@ -79,7 +79,7 @@ class SwiftOptions { } /// Options that control how Swift code will be generated. -class InternalSwiftOptions { +class InternalSwiftOptions extends PigeonInternalOptions { /// Creates a [InternalSwiftOptions] object const InternalSwiftOptions({ this.copyrightHeader, From be3ca11cdd564c956134d4912bb4f3187c5de89a Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Mon, 24 Mar 2025 15:36:45 -0700 Subject: [PATCH 5/6] general use method --- packages/pigeon/lib/src/dart/dart_generator.dart | 15 +++------------ packages/pigeon/lib/src/generator_tools.dart | 8 ++++++++ .../pigeon/lib/src/kotlin/kotlin_generator.dart | 14 +++----------- 3 files changed, 14 insertions(+), 23 deletions(-) diff --git a/packages/pigeon/lib/src/dart/dart_generator.dart b/packages/pigeon/lib/src/dart/dart_generator.dart index 9fa7260ce03..b8897e40f82 100644 --- a/packages/pigeon/lib/src/dart/dart_generator.dart +++ b/packages/pigeon/lib/src/dart/dart_generator.dart @@ -354,12 +354,7 @@ class DartGenerator extends StructuredGenerator { indent.writeScoped('return ', '', () { indent.format( classDefinition.fields - .map((NamedType field) => field.type.baseName == 'List' || - field.type.baseName == 'Float64List' || - field.type.baseName == 'Int32List' || - field.type.baseName == 'Int64List' || - field.type.baseName == 'Uint8List' || - field.type.baseName == 'Map' + .map((NamedType field) => isCollectionType(field.type) ? '_deepEquals(${field.name}, other.${field.name})' : '${field.name} == other.${field.name}') .join('\n&& '), @@ -1089,12 +1084,8 @@ final BinaryMessenger? ${varNamePrefix}binaryMessenger; _writeWrapResponse(generatorOptions, root, indent); } if (root.classes.isNotEmpty && - root.classes.any((Class dataClass) => dataClass.fields.any( - (NamedType field) => - !field.type.isClass && - !field.type.isEnum && - (field.type.baseName.contains('List') || - field.type.baseName.startsWith('Map'))))) { + root.classes.any((Class dataClass) => dataClass.fields + .any((NamedType field) => isCollectionType(field.type)))) { _writeDeepEquals(indent); } } diff --git a/packages/pigeon/lib/src/generator_tools.dart b/packages/pigeon/lib/src/generator_tools.dart index 94dc56c8f02..dc71f59fcb4 100644 --- a/packages/pigeon/lib/src/generator_tools.dart +++ b/packages/pigeon/lib/src/generator_tools.dart @@ -861,3 +861,11 @@ String makeClearChannelName(String dartPackageName) { dartPackageName: dartPackageName, ); } + +/// Whether the type is a collection. +bool isCollectionType(TypeDeclaration type) { + return !type.isClass && + !type.isEnum && + !type.isProxyApi && + (type.baseName.contains('List') || type.baseName.startsWith('Map')); +} diff --git a/packages/pigeon/lib/src/kotlin/kotlin_generator.dart b/packages/pigeon/lib/src/kotlin/kotlin_generator.dart index df9fe78b719..630255dc9c7 100644 --- a/packages/pigeon/lib/src/kotlin/kotlin_generator.dart +++ b/packages/pigeon/lib/src/kotlin/kotlin_generator.dart @@ -318,12 +318,7 @@ class KotlinGenerator extends StructuredGenerator { }); indent.write('return '); indent.format(classDefinition.fields - .map((NamedType field) => field.type.baseName == 'List' || - field.type.baseName == 'Float64List' || - field.type.baseName == 'Int32List' || - field.type.baseName == 'Int64List' || - field.type.baseName == 'Uint8List' || - field.type.baseName == 'Map' + .map((NamedType field) => isCollectionType(field.type) ? 'deepEquals${generatorOptions.fileSpecificClassNameComponent}(${field.name}, other.${field.name})' : '${field.name} == other.${field.name}') .join('\n&& ')); @@ -1306,11 +1301,8 @@ private fun deepEquals${generatorOptions.fileSpecificClassNameComponent}(a: Any? _writeErrorClass(generatorOptions, indent); } if (root.classes.isNotEmpty && - root.classes.any((Class dataClass) => dataClass.fields.any( - (NamedType field) => - _kotlinTypeForBuiltinDartType(field.type) != null && - (field.type.baseName.contains('List') || - field.type.baseName.startsWith('Map'))))) { + root.classes.any((Class dataClass) => dataClass.fields + .any((NamedType field) => isCollectionType(field.type)))) { _writeDeepEquals(generatorOptions, indent); } } From d7dc94c9aea275ca30eb6bba54968faad10753d8 Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Tue, 25 Mar 2025 10:59:25 -0700 Subject: [PATCH 6/6] nits --- packages/pigeon/lib/src/generator_tools.dart | 2 +- .../pigeon/lib/src/kotlin/kotlin_generator.dart | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/pigeon/lib/src/generator_tools.dart b/packages/pigeon/lib/src/generator_tools.dart index dc71f59fcb4..58cc1abc99f 100644 --- a/packages/pigeon/lib/src/generator_tools.dart +++ b/packages/pigeon/lib/src/generator_tools.dart @@ -867,5 +867,5 @@ bool isCollectionType(TypeDeclaration type) { return !type.isClass && !type.isEnum && !type.isProxyApi && - (type.baseName.contains('List') || type.baseName.startsWith('Map')); + (type.baseName.contains('List') || type.baseName == 'Map'); } diff --git a/packages/pigeon/lib/src/kotlin/kotlin_generator.dart b/packages/pigeon/lib/src/kotlin/kotlin_generator.dart index 630255dc9c7..1368fa351de 100644 --- a/packages/pigeon/lib/src/kotlin/kotlin_generator.dart +++ b/packages/pigeon/lib/src/kotlin/kotlin_generator.dart @@ -317,11 +317,14 @@ class KotlinGenerator extends StructuredGenerator { indent.writeln('return true'); }); indent.write('return '); - indent.format(classDefinition.fields - .map((NamedType field) => isCollectionType(field.type) - ? 'deepEquals${generatorOptions.fileSpecificClassNameComponent}(${field.name}, other.${field.name})' - : '${field.name} == other.${field.name}') - .join('\n&& ')); + indent.format( + classDefinition.fields + .map((NamedType field) => isCollectionType(field.type) + ? 'deepEquals${generatorOptions.fileSpecificClassNameComponent}(${field.name}, other.${field.name})' + : '${field.name} == other.${field.name}') + .join('\n&& '), + leadingSpace: false, + ); }); indent.newln();