Skip to content

[pigeon] kotlin equality methods #8887

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Mar 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/pigeon/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand All @@ -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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Any?, Any?>).containsKey(it) && deepEqualsMessages(a[it], b[it]) }
}
return a == b
}

enum class Code(val raw: Int) {
ONE(0),
TWO(1);
Expand Down Expand Up @@ -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() {
Expand Down
2 changes: 1 addition & 1 deletion packages/pigeon/lib/src/cpp/cpp_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
15 changes: 4 additions & 11 deletions packages/pigeon/lib/src/dart/dart_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -354,12 +354,7 @@ class DartGenerator extends StructuredGenerator<InternalDartOptions> {
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&& '),
Expand Down Expand Up @@ -1089,10 +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.baseName.startsWith('List') ||
field.type.baseName.startsWith('Map')))) {
root.classes.any((Class dataClass) => dataClass.fields
.any((NamedType field) => isCollectionType(field.type)))) {
_writeDeepEquals(indent);
}
}
Expand Down
57 changes: 30 additions & 27 deletions packages/pigeon/lib/src/generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,36 @@ 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<InternalOptions> {
abstract class Generator<PigeonInternalOptions> {
/// Constructor.
const Generator();

/// Generates files for specified language with specified [generatorOptions]
void generate(
InternalOptions generatorOptions,
PigeonInternalOptions generatorOptions,
Root root,
StringSink sink, {
required String dartPackageName,
});
}

/// An abstract base class that enforces code generation across platforms.
abstract class StructuredGenerator<InternalOptions>
extends Generator<InternalOptions> {
abstract class StructuredGenerator<PigeonInternalOptions>
extends Generator<PigeonInternalOptions> {
/// Constructor.
const StructuredGenerator();

@override
void generate(
InternalOptions generatorOptions,
PigeonInternalOptions generatorOptions,
Root root,
StringSink sink, {
required String dartPackageName,
Expand Down Expand Up @@ -123,15 +126,15 @@ abstract class StructuredGenerator<InternalOptions>

/// Adds specified headers to [indent].
void writeFilePrologue(
InternalOptions generatorOptions,
PigeonInternalOptions generatorOptions,
Root root,
Indent indent, {
required String dartPackageName,
});

/// Writes specified imports to [indent].
void writeFileImports(
InternalOptions generatorOptions,
PigeonInternalOptions generatorOptions,
Root root,
Indent indent, {
required String dartPackageName,
Expand All @@ -141,7 +144,7 @@ abstract class StructuredGenerator<InternalOptions>
///
/// 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,
Expand All @@ -151,7 +154,7 @@ abstract class StructuredGenerator<InternalOptions>
///
/// 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,
Expand All @@ -161,7 +164,7 @@ abstract class StructuredGenerator<InternalOptions>
///
/// 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,
Expand All @@ -171,7 +174,7 @@ abstract class StructuredGenerator<InternalOptions>
///
/// Can be overridden to add extra code before/after enums.
void writeEnums(
InternalOptions generatorOptions,
PigeonInternalOptions generatorOptions,
Root root,
Indent indent, {
required String dartPackageName,
Expand All @@ -189,7 +192,7 @@ abstract class StructuredGenerator<InternalOptions>

/// Writes a single Enum to [indent]. This is needed in most generators.
void writeEnum(
InternalOptions generatorOptions,
PigeonInternalOptions generatorOptions,
Root root,
Indent indent,
Enum anEnum, {
Expand All @@ -200,7 +203,7 @@ abstract class StructuredGenerator<InternalOptions>
///
/// Can be overridden to add extra code before/after apis.
void writeDataClasses(
InternalOptions generatorOptions,
PigeonInternalOptions generatorOptions,
Root root,
Indent indent, {
required String dartPackageName,
Expand All @@ -218,15 +221,15 @@ abstract class StructuredGenerator<InternalOptions>

/// Writes the custom codec to [indent].
void writeGeneralCodec(
InternalOptions generatorOptions,
PigeonInternalOptions generatorOptions,
Root root,
Indent indent, {
required String dartPackageName,
});

/// Writes a single data class to [indent].
void writeDataClass(
InternalOptions generatorOptions,
PigeonInternalOptions generatorOptions,
Root root,
Indent indent,
Class classDefinition, {
Expand All @@ -235,7 +238,7 @@ abstract class StructuredGenerator<InternalOptions>

/// Writes a single class encode method to [indent].
void writeClassEncode(
InternalOptions generatorOptions,
PigeonInternalOptions generatorOptions,
Root root,
Indent indent,
Class classDefinition, {
Expand All @@ -244,7 +247,7 @@ abstract class StructuredGenerator<InternalOptions>

/// Writes a single class decode method to [indent].
void writeClassDecode(
InternalOptions generatorOptions,
PigeonInternalOptions generatorOptions,
Root root,
Indent indent,
Class classDefinition, {
Expand All @@ -253,7 +256,7 @@ abstract class StructuredGenerator<InternalOptions>

/// Writes a single class decode method to [indent].
void writeClassEquality(
InternalOptions generatorOptions,
PigeonInternalOptions generatorOptions,
Root root,
Indent indent,
Class classDefinition, {
Expand All @@ -264,7 +267,7 @@ abstract class StructuredGenerator<InternalOptions>
///
/// Can be overridden to add extra code before/after classes.
void writeApis(
InternalOptions generatorOptions,
PigeonInternalOptions generatorOptions,
Root root,
Indent indent, {
required String dartPackageName,
Expand Down Expand Up @@ -309,7 +312,7 @@ abstract class StructuredGenerator<InternalOptions>

/// Writes a single Flutter Api to [indent].
void writeFlutterApi(
InternalOptions generatorOptions,
PigeonInternalOptions generatorOptions,
Root root,
Indent indent,
AstFlutterApi api, {
Expand All @@ -318,7 +321,7 @@ abstract class StructuredGenerator<InternalOptions>

/// Writes a single Host Api to [indent].
void writeHostApi(
InternalOptions generatorOptions,
PigeonInternalOptions generatorOptions,
Root root,
Indent indent,
AstHostApi api, {
Expand All @@ -327,7 +330,7 @@ abstract class StructuredGenerator<InternalOptions>

/// Writes the implementation of an `InstanceManager` to [indent].
void writeInstanceManager(
InternalOptions generatorOptions,
PigeonInternalOptions generatorOptions,
Root root,
Indent indent, {
required String dartPackageName,
Expand All @@ -336,7 +339,7 @@ abstract class StructuredGenerator<InternalOptions>
/// Writes the implementation of the API for the `InstanceManager` to
/// [indent].
void writeInstanceManagerApi(
InternalOptions generatorOptions,
PigeonInternalOptions generatorOptions,
Root root,
Indent indent, {
required String dartPackageName,
Expand All @@ -353,14 +356,14 @@ abstract class StructuredGenerator<InternalOptions>
/// 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, {
Expand All @@ -369,7 +372,7 @@ abstract class StructuredGenerator<InternalOptions>

/// Writes a single event channel Api to [indent].
void writeEventChannelApi(
InternalOptions generatorOptions,
PigeonInternalOptions generatorOptions,
Root root,
Indent indent,
AstEventChannelApi api, {
Expand Down
10 changes: 9 additions & 1 deletion packages/pigeon/lib/src/generator_tools.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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 == 'Map');
}
Loading