Skip to content

[pigeon] Optimize data class equality and hashing in Dart, Kotlin, java, and Swift, adds equality in other languages#11140

Merged
auto-submit[bot] merged 44 commits intoflutter:mainfrom
tarrinneal:effic
Mar 25, 2026
Merged

[pigeon] Optimize data class equality and hashing in Dart, Kotlin, java, and Swift, adds equality in other languages#11140
auto-submit[bot] merged 44 commits intoflutter:mainfrom
tarrinneal:effic

Conversation

@tarrinneal
Copy link
Copy Markdown
Contributor

@tarrinneal tarrinneal commented Feb 28, 2026

This PR optimizes the generated equality (==) and hashing (hashCode/hash) logic for data classes across All languages. These have been bothering me for a while, and I wanted to break out a new pr before the NI code is in review.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces significant performance optimizations for data class equality and hashing in Dart, Kotlin, and Swift by avoiding the creation of intermediate lists. The changes are well-implemented and include necessary updates to helper utilities like deepEquals and deepHash for robust recursive comparisons. The addition of generator tests for each language is a great way to ensure the new logic is correct. I have a couple of minor suggestions to improve the generator code's maintainability.

@tarrinneal tarrinneal changed the title [pigeon] Optimize data class equality and hashing in Dart, Kotlin, and Swift [pigeon] Optimize data class equality and hashing in Dart, Kotlin, and Swift, adds equality in other languages Mar 4, 2026
@tarrinneal
Copy link
Copy Markdown
Contributor Author

/gemini review

@tarrinneal
Copy link
Copy Markdown
Contributor Author

@stuartmorgan-g this should be ready for review

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a significant and wide-ranging change to optimize and standardize equality and hashing for data classes across all supported languages. It adds new helper functions for deep equality and hashing, which is a great step towards cross-platform consistency. However, I've found a critical logic bug in the new map equality implementation across several language generators (Dart, Kotlin, Swift, Objective-C, C++) that could lead to incorrect results. Additionally, the C++ map hashing implementation is inconsistent with other platforms, which undermines the goal of uniform hashing. These issues need to be addressed before merging.

Note: Security Review did not run due to the size of the PR.

return pigeonDeepEquals(name, that.name)
&& pigeonDeepEquals(description, that.description)
&& pigeonDeepEquals(code, that.code)
&& pigeonDeepEquals(data, that.data);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Capturing for posterity, this has the same inefficiency as the previous iteration, where we are feeding known-type values through a big type-inspection chain instead of dispatching to specific-type equality checkers.

Not something we need to address now since we still mostly expect these to be used in tests where efficiency doesn't matter, but something to keep in mind later if it performance ever comes up.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't actually, any non-collections well return first before any type checking. Even collections are only checking for collection types.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're probably thinking about the codec, which I intend to change so known types bypass the type checking soon(tm)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't actually, any non-collections well return first before any type checking.

I'm not following. The first line in this statement is calling pigeonDeepEquals with two strings. We know at compile time they are strings. But we are calling pigeonDeepEquals for them which will do runtime checks for whether they are:

  • byte arrays
  • int arrays
  • long arrays
  • double arrays
  • Lists
  • Maps
  • Doubles
  • Floats

and only then, after all of that, check that the strings are equal.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the first thing pigeon deep equals does is return if identical

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, but that's only pointer equality for most types. For two instances of "Foo", a == b at the top is false, only a.equals(b) at the very end is true, after all the type checks. Similarly, two Map instance variables that aren't actually the same map, but will evaluate to the same under the deep equality evaluation, will go through 5 pointless type checks first. Similarly with List. Similarly with Double and Float.

Basically, in any case where we actually need all this logic in the first place and can't just rely on == alone, we are doing a bunch of type checks.

Copy link
Copy Markdown
Contributor Author

@tarrinneal tarrinneal Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For two instances of "Foo", a == b at the top is false, only a.equals(b) at the very end is true

That is a detail I missed. I've resolved this in every language (that I can).

My future work for changing the codec calls to skip the type checking (this will be relevant once native interop lands) will change this to avoid type checking also.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think the quick fix of moving the non-trivial equality check to the top is something we want to do, because it may be non-trivially expensive, and duplicative of specific checks. E.g., I'm almost positive this will compare non-equal strings twice in Obj-C instead of once, which is potentially more expensive than the thing I was describing in the initial comment.

The fix here (which again, doesn't need to be done now, I just want to capture it for later reference) isn't to change how pigeonDeepEquals works, it's to not generate calls to pigeonDeepEquals in the first place when we already know the type. Instead, all the specific checks should be helpers (pigeonMapDeepEquals, etc.), pigeonDeepEquals should be implemented using those helpers, and the generator for class equality should be calling the correct helper for each field instead of the generic helper.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is true, reverted.

@tarrinneal
Copy link
Copy Markdown
Contributor Author

@stuartmorgan-g your comments are addressed, I'm still planning on reading up a bit on things before landing.

Copy link
Copy Markdown
Collaborator

@stuartmorgan-g stuartmorgan-g left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor notes (mostly orthodontia), but otherwise LGTM.

return pigeonDeepEquals(name, that.name)
&& pigeonDeepEquals(description, that.description)
&& pigeonDeepEquals(code, that.code)
&& pigeonDeepEquals(data, that.data);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't actually, any non-collections well return first before any type checking.

I'm not following. The first line in this statement is calling pigeonDeepEquals with two strings. We know at compile time they are strings. But we are calling pigeonDeepEquals for them which will do runtime checks for whether they are:

  • byte arrays
  • int arrays
  • long arrays
  • double arrays
  • Lists
  • Maps
  • Doubles
  • Floats

and only then, after all of that, check that the strings are equal.

@tarrinneal
Copy link
Copy Markdown
Contributor Author

(mostly orthodontia)

Can we add a gobject lint tool?

@tarrinneal
Copy link
Copy Markdown
Contributor Author

@stuartmorgan-g done done

@tarrinneal
Copy link
Copy Markdown
Contributor Author

@stuartmorgan-g While I technically need your review. Gemini said: "You have essentially achieved the holy grail of cross-platform plugin design. You maintained strict, identical behavioral output while perfectly adapting to the efficiency constraints and memory safety rules of each specific compiler."

So I think I'm set.

@tarrinneal tarrinneal added the CICD Run CI/CD label Mar 12, 2026
Copy link
Copy Markdown
Collaborator

@stuartmorgan-g stuartmorgan-g left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@tarrinneal tarrinneal added the autosubmit Merge PR when tree becomes green via auto submit App label Mar 20, 2026
@tarrinneal tarrinneal added CICD Run CI/CD and removed CICD Run CI/CD labels Mar 25, 2026
@auto-submit auto-submit bot merged commit 3e15247 into flutter:main Mar 25, 2026
81 checks passed
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Mar 25, 2026
…Kotlin, java, and Swift, adds equality in other languages (flutter/packages#11140)
github-merge-queue bot pushed a commit to flutter/flutter that referenced this pull request Mar 25, 2026
flutter/packages@8dcfd11...5909bdd

2026-03-25 47866232+chunhtai@users.noreply.github.com [ci] add more
permissions for create-pull-request (flutter/packages#11302)
2026-03-25 stuartmorgan@google.com [various] Add
`unintended_html_in_doc_comment` to analysis options
(flutter/packages#11303)
2026-03-25 matt.boetger@gmail.com Use deprecated dependency until legacy
renderer is removed (flutter/packages#11185)
2026-03-25 louisehsu@google.com [in_app_purchase_storekit] Address flaky
tests (flutter/packages#11270)
2026-03-25 stuartmorgan@google.com [google_maps_flutter] Fix A2A iOS
builds (flutter/packages#11290)
2026-03-25 stuartmorgan@google.com [mustache_template] Fix broken README
link (flutter/packages#11306)
2026-03-25 stuartmorgan@google.com [ci] Add a workflow to auto-remove
CICD label (flutter/packages#11301)
2026-03-25 stuartmorgan@google.com [various] Convert plugin builds to
Kotlin gradle (flutter/packages#11172)
2026-03-25 spkhalad@gmail.com [webview_flutter_platform_interface] Add
support for getting cookie (flutter/packages#11037)
2026-03-25 stuartmorgan@google.com [cupertino_icons] Remove empty Dart
file (flutter/packages#11308)
2026-03-25 stuartmorgan@google.com [camera] Regenerate iOS example with
Swift (flutter/packages#11283)
2026-03-25 tarrinneal@gmail.com [pigeon] Optimize data class equality
and hashing in Dart, Kotlin, java, and Swift, adds equality in other
languages (flutter/packages#11140)
2026-03-25 katelovett@google.com [two_dimensional_scrollables] Fix span
border decorations in flipped cross axes (flutter/packages#11334)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-packages-flutter-autoroll
Please CC flutter-ecosystem@google.com on the revert to ensure that a
human
is aware of the problem.

To file a bug in Flutter:
https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
okorohelijah pushed a commit to okorohelijah/packages that referenced this pull request Mar 26, 2026
…va, and Swift, adds equality in other languages (flutter#11140)

This PR optimizes the generated equality (==) and hashing (hashCode/hash) logic for data classes across All languages. These have been bothering me for a while, and I wanted to break out a new pr before the NI code is in review.
mboetger pushed a commit to mboetger/flutter that referenced this pull request Mar 26, 2026
…er#184123)

flutter/packages@8dcfd11...5909bdd

2026-03-25 47866232+chunhtai@users.noreply.github.com [ci] add more
permissions for create-pull-request (flutter/packages#11302)
2026-03-25 stuartmorgan@google.com [various] Add
`unintended_html_in_doc_comment` to analysis options
(flutter/packages#11303)
2026-03-25 matt.boetger@gmail.com Use deprecated dependency until legacy
renderer is removed (flutter/packages#11185)
2026-03-25 louisehsu@google.com [in_app_purchase_storekit] Address flaky
tests (flutter/packages#11270)
2026-03-25 stuartmorgan@google.com [google_maps_flutter] Fix A2A iOS
builds (flutter/packages#11290)
2026-03-25 stuartmorgan@google.com [mustache_template] Fix broken README
link (flutter/packages#11306)
2026-03-25 stuartmorgan@google.com [ci] Add a workflow to auto-remove
CICD label (flutter/packages#11301)
2026-03-25 stuartmorgan@google.com [various] Convert plugin builds to
Kotlin gradle (flutter/packages#11172)
2026-03-25 spkhalad@gmail.com [webview_flutter_platform_interface] Add
support for getting cookie (flutter/packages#11037)
2026-03-25 stuartmorgan@google.com [cupertino_icons] Remove empty Dart
file (flutter/packages#11308)
2026-03-25 stuartmorgan@google.com [camera] Regenerate iOS example with
Swift (flutter/packages#11283)
2026-03-25 tarrinneal@gmail.com [pigeon] Optimize data class equality
and hashing in Dart, Kotlin, java, and Swift, adds equality in other
languages (flutter/packages#11140)
2026-03-25 katelovett@google.com [two_dimensional_scrollables] Fix span
border decorations in flipped cross axes (flutter/packages#11334)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-packages-flutter-autoroll
Please CC flutter-ecosystem@google.com on the revert to ensure that a
human
is aware of the problem.

To file a bug in Flutter:
https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants