Repository navigation
[pigeon] Reject unsupported native interop features and fix generated code that doesn't compile - #13153
Conversation
There was a problem hiding this comment.
Code Review
This pull request updates Pigeon to version 29.0.7, adding validation checks for unsupported Swift FFI and Kotlin JNI features, such as event channels, ProxyApis, sealed classes, and conflicting member or parameter names. It also fixes compilation issues in Swift FFI and Dart native interop, including parameter shadowing, empty classes, and JNIgen member renaming, and adds corresponding unit tests. There are no review comments, and no feedback is provided.
dkwingsmt
left a comment
There was a problem hiding this comment.
LGTM. I tried my best to understand the PR and I don't see any major flaws.
The only thing I'm worried about is relying on the exact implementation of JNIgen and hardcoded declaredFirst, which might mismatch the versions used by users in the future. Although, they might be stable for a long time, which is enough reason for this PR to land now.
Also,
A file that generated before can now fail validation, but native interop is still marked experimental.
Should we note this in the changelog?
I think realistically, those files that passed validation wouldn't have been functional. As for the mimicking of jnigen behavior, until jnigen allows access to the methods used to make these outputs, I can't really do anything else. I'm planing on requesting access to them when @liamappelbe is back from vacation. |
flutter/packages@54706bd...d8d9e97 2026-10-09 [email protected] [pigeon] Reject unsupported native interop features and fix generated code that doesn't compile (flutter/packages#13153) 2026-10-09 [email protected] [material_ui] Fix ExpansionTile WidgetSpan semantics assertion (flutter/packages#12765) 2026-10-08 [email protected] [in_app_purchase_storekit] Complete StoreKit 2 `finish` when the transaction is not found (flutter/packages#13037) 2026-10-08 [email protected] [video_player] Fix crash when videoTrack has zero-duration minFrameDuration (flutter/packages#12796) 2026-10-08 [email protected] [google_maps_flutter_platform_interface] Add map background color configuration (flutter/packages#13014) If this roll has caused a breakage, revert this CL and set the roller to dry run mode 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@54706bd...d8d9e97 2026-10-09 [email protected] [pigeon] Reject unsupported native interop features and fix generated code that doesn't compile (flutter/packages#13153) 2026-10-09 [email protected] [material_ui] Fix ExpansionTile WidgetSpan semantics assertion (flutter/packages#12765) 2026-10-08 [email protected] [in_app_purchase_storekit] Complete StoreKit 2 `finish` when the transaction is not found (flutter/packages#13037) 2026-10-08 [email protected] [video_player] Fix crash when videoTrack has zero-duration minFrameDuration (flutter/packages#12796) 2026-10-08 [email protected] [google_maps_flutter_platform_interface] Add map background color configuration (flutter/packages#13014) If this roll has caused a breakage, revert this CL and set the roller to dry run mode 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
Pigeon accepted several combinations of
swift_use_ffiorkotlin_use_jniwith other features and options that generated code that doesn't compile. One combination crashed pigeon. flutter/flutter#193881 reported two of them: event channels and ProxyApis with Swift FFI.To find the rest, I generated every repo pigeon input and a set of edge-case inputs with each interop option. I then type-checked the Swift, compiled the Kotlin and analyzed the Dart. That found 19 broken combinations, and fixing them found a few more.
This PR adds a validation error where native interop has no implementation yet, and fixes the generators for everything else. It also adds a test API to the test plugin that uses the names JNIgen renames, so the integration tests check pigeon's copy of JNIgen's renaming rules at runtime.
New validation errors. Apart from event channels and ProxyApis with Kotlin JNI, none of these combinations ever produced code that compiled.
swift_use_ffi.NSObjectmembers, because FFI bridge classes extendNSObject. Examples arehash,isProxyandsuperclass, plus names that ffigen renames, such asallocorisEqual.wrappedError. ffigen would generate twowrappedError:named parameters.includeErrorClass: false. swiftgen compiles each Swift file on its own, so the file must declare its error class. Give each file a differenterrorClassNameinstead.kotlin_use_jni.getInstanceandcreateWithNativeInteropApi.implement, and with JNI alsodartApiandregister.Generator fixes
init()was missingoverride.@asyncCallbackhost methods: the bridgeawaited a callback-style method. It now wraps the call inwithCheckedThrowingContinuation.@SwiftFunctionoverloads that differ only in nullability were ambiguous. The bridge now declares the result type.typebroke error handling in every method of that API, becausetype(of:)resolved to the method. The bridge now usesSwift.type(of:).error, collided with it. Internal parameter names now end inArg. Selectors don't change._PigeonFfiCodec. It's nowprivate.Pigeon crashed with
Bad state: No elementwhen a class without fields was used with Swift FFI.JNI calls used the Kotlin member names, but JNIgen changes some of them:
type,release,useandisNull.getX(),isX()andsetX()into properties.is-prefixed data class properties that aren'tBooleaninto methods.A new
_JniBindingNamermirrors the JNIgen 1.0.1 renamer for data class fields, HostApi methods and FlutterApi overrides. It replaces_getJniFieldName, which handled only a few field names.FFI calls assumed that Swift's Objective-C selectors always join the method name and the first argument label with
With. Swift leaves outWithwhen the method name ends with a preposition or the label starts with one. For example,signIn(value:)becomessignInValue:andinit(forKey:)becomesinitForKey:. Dart now applies the same rule to host API calls, FlutterApi callbacks and data class bridge initializers. It uses the preposition list from Swift'sPartsOfSpeech.defand Swift's camel-case word splitting.Generated local variables shadowed parameters with the same name, such as
res,error,completerandresponse. Generated locals now use thepigeonVar_prefix, and callback parameters usearg_.Lists and maps of ProxyApis caused the interop codecs to contain the literal text
There is something wrong, a type is not classified. The codecs now skip those types.The class-name prefix for FFI types didn't match the Swift one in three cases:
fileSpecificClassNameComponentwasn't UpperCamelCase.SwiftOptions.fileSpecificClassNameComponentwas set.Dart now uses the same precedence and casing as the Swift generator.
Test files (
dartTestOut) contained native interop code that referenced bindings they don't import. They're now generated with method channels only, which is all their mock handlers use.includeErrorClassdocs onSwiftOptionsandKotlinOptionsexplain the native interop requirements.Test plugin
native_interop_tests.dartaddsNativeInteropReservedNames,NativeInteropReservedNamesHostApiandNativeInteropReservedNamesFlutterApi. Their names cover each JNIgen renaming rule:release,use,type,isNull,referenceandjClass;ismethods that JNIgen turns into properties, includinggetDefault, which becomes a Dart keyword;isfields that aren'tbool, which JNIgen turns into methods;@asyncmethod, which JNIgen keeps as a method because it's a Kotlinsuspendfunction;implementIn, which clashes with JNIgen'simplementIn.The Kotlin and Swift test plugins implement both APIs, and new integration tests call every method and round-trip the data class. The tests use
trueforisNullandisReleased, so they fail if the generated code callsJObject's own members instead, which returnfalse. This API is also how I found the Swift selector bug above:implementInends with a preposition.The test plugin and the native interop example are regenerated. Apart from the new test API, their diffs contain only:
privateon_PigeonFfiCodec;Swift.type(of:);Selectors and public APIs don't change.
Testing
swiftc, for every preposition and for names with underscores, digits and acronyms.swiftc.Fixes flutter/flutter#193881
Pre-Review Checklist
[shared_preferences]///).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