-
Notifications
You must be signed in to change notification settings - Fork 3.5k
[pigeon] Fixes support for Kotlin/Java classes that override equals and hashCode for ProxyApis #10039
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
Conversation
|
/gemini review |
There was a problem hiding this 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 effectively addresses a bug in the Kotlin InstanceManager where classes overriding equals() and hashCode() could cause incorrect instance tracking. The introduction of the IdentityKey wrapper to enforce identity-based comparisons in the underlying WeakHashMap is a clever solution to the lack of a built-in IdentityWeakHashMap. The addition of new tests specifically for this scenario is also a great way to ensure the fix is robust. Furthermore, adding logging for failures during Dart proxy instance creation improves debuggability. I've found one critical issue in the implementation of the new IdentityKey class that should be addressed.
| override fun hashCode(): Int { | ||
| return instance.get().hashCode() | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The hashCode implementation for IdentityKey has a critical bug and a logical inconsistency.
- NPE Bug: If the
WeakReferencehas been cleared by the garbage collector,instance.get()will returnnull. Calling.hashCode()onnullwill result in aNullPointerException. Theequalsmethod correctly handles this case, buthashCodedoes not. - Logical Inconsistency: The purpose of
IdentityKeyis to enforce identity-based equality, similar toIdentityHashMap. Therefore, its hash code should also be based on identity. Using the instance's ownhashCode()can lead to unnecessary hash collisions if different instances have the same value-based hash code, and it deviates from the principle of identity-based hashing.
The correct approach is to use System.identityHashCode(), which is safe for null inputs (it returns 0) and provides an identity-based hash code.
| override fun hashCode(): Int { | |
| return instance.get().hashCode() | |
| } | |
| override fun hashCode(): Int { | |
| return System.identityHashCode(instance.get()) | |
| } |
|
/gemini review |
There was a problem hiding this 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 addresses a critical bug in the Kotlin/Java InstanceManager for ProxyApis where classes overriding equals() and hashCode() could cause incorrect instance tracking. The fix introduces an IdentityWeakReference wrapper to ensure instance management is based on object identity rather than equality, which is a robust solution to the problem. The changes are well-tested with new unit tests covering the identity comparison logic and edge cases related to garbage collection. Additionally, the PR improves diagnostics by adding logging for failed Dart proxy instance creations. The overall implementation is solid. I have one suggestion to reduce code duplication in the generated code for the new logging feature.
| registrar.get$hostProxyApiPrefix${api.name}().${classMemberNamePrefix}newInstance(value) { | ||
| if (it.isFailure) { | ||
| Log.w( | ||
| "${proxyApiCodecName(const InternalKotlinOptions(kotlinOut: ''))}", | ||
| "Failed to create new Dart proxy instance of ${api.name}: \$value. \${it.exceptionOrNull()}") | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While this logging is a great addition for debugging, the current implementation in the generator leads to duplicated code blocks in the generated writeValue method for each ProxyApi type. To improve the maintainability and readability of the generated code, consider refactoring this. You could generate a private helper method within the ProxyApiBaseCodec class to encapsulate the logging logic, and then call that helper from each if block.
For example, you could generate a method like this:
private fun logNewInstanceFailure(value: Any, apiName: String, result: Result<*>) {
if (result.isFailure) {
Log.w(
"PigeonProxyApiBaseCodec",
"Failed to create new Dart proxy instance of $apiName: $value. ${result.exceptionOrNull()}"
)
}
}Then, the call site in the if block would be a single, clean line.
…e equals and hashCode for ProxyApis (flutter/packages#10039)
…e equals and hashCode for ProxyApis (flutter/packages#10039)
…e equals and hashCode for ProxyApis (flutter/packages#10039)
…e equals and hashCode for ProxyApis (flutter/packages#10039)
…e equals and hashCode for ProxyApis (flutter/packages#10039)
…e equals and hashCode for ProxyApis (flutter/packages#10039)
flutter/packages@d062181...835dccb 2025-10-16 [email protected] [local_auth] Adopt structured errors and remove useErrorDialogs - platform implementations (flutter/packages#10147) 2025-10-16 49699333+dependabot[bot]@users.noreply.github.com [dependabot]: Bump org.mockito.kotlin:mockito-kotlin from 6.0.0 to 6.1.0 in /packages/interactive_media_ads/android (flutter/packages#10221) 2025-10-15 [email protected] Manual roll Flutter from e11e2c1 to 7cd821c (73 revisions) (flutter/packages#10229) 2025-10-15 [email protected] [go_router] feat: access GoRouter.of from redirect methods (flutter/packages#9706) 2025-10-14 [email protected] [Many] Migrate non examples (and pigeon test) to java 17 (flutter/packages#10201) 2025-10-13 49699333+dependabot[bot]@users.noreply.github.com [dependabot]: Bump com.google.ads.interactivemedia.v3:interactivemedia from 3.36.0 to 3.37.0 in /packages/interactive_media_ads/android (flutter/packages#10113) 2025-10-13 [email protected] [pigeon] Fixes support for Kotlin/Java classes that override equals and hashCode for ProxyApis (flutter/packages#10039) 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 [email protected] 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
flutter/packages@d062181...835dccb 2025-10-16 [email protected] [local_auth] Adopt structured errors and remove useErrorDialogs - platform implementations (flutter/packages#10147) 2025-10-16 49699333+dependabot[bot]@users.noreply.github.com [dependabot]: Bump org.mockito.kotlin:mockito-kotlin from 6.0.0 to 6.1.0 in /packages/interactive_media_ads/android (flutter/packages#10221) 2025-10-15 [email protected] Manual roll Flutter from e11e2c1 to 7cd821c (73 revisions) (flutter/packages#10229) 2025-10-15 [email protected] [go_router] feat: access GoRouter.of from redirect methods (flutter/packages#9706) 2025-10-14 [email protected] [Many] Migrate non examples (and pigeon test) to java 17 (flutter/packages#10201) 2025-10-13 49699333+dependabot[bot]@users.noreply.github.com [dependabot]: Bump com.google.ads.interactivemedia.v3:interactivemedia from 3.36.0 to 3.37.0 in /packages/interactive_media_ads/android (flutter/packages#10113) 2025-10-13 [email protected] [pigeon] Fixes support for Kotlin/Java classes that override equals and hashCode for ProxyApis (flutter/packages#10039) 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 [email protected] 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
ProxyApis for classes that override
equals()/hashcodein Kotlin/Java were breaking theInstanceManager.The Kotlin
InstanceManagerwould:containsInstance.getIdentifierForStrongReference.This changes the the use of
WeakReferenceto a newIdentityWeakReferencethat uses identity to check for equality. Java/Kotlin has anIdentityHashMapand aWeakHashMap, but not anIdentityWeakHashMap. So this is a quick workaround to create an equivalent.Alternatively,
getIdentifierForStrongReferencecould be updated to checkcontainsInstancefirst, but this would leave small timeframes where objects would only have a weak reference. And it seems possible that Kotlin GC could cleanup at any moment.Potential fix for flutter/flutter#174134 since the class they were creating a ProxyApi for was a data class: https://github.com/JavesonYehudi/proxy_bug_report/blob/main/android/src/main/kotlin/com/example/proxy_bug_report/auth/Credentials.kt
Pre-Review Checklist
[shared_preferences]pubspec.yamlwith an appropriate new version according to the pub versioning philosophy, or I have commented below to indicate which version change exemption this PR falls under1.CHANGELOG.mdto add a description of the change, following repository CHANGELOG style, or I have commented below to indicate which CHANGELOG exemption this PR falls under1.///).If you need help, consider asking for advice on the #hackers-new channel on Discord.
Note: The Flutter team is currently trialing the use of Gemini Code Assist for GitHub. Comments from the
gemini-code-assistbot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed.Footnotes
Regular contributors who have demonstrated familiarity with the repository guidelines only need to comment if the PR is not auto-exempted by repo tooling. ↩ ↩2 ↩3