Skip to content

[pigeon] Fix NSnumber edge cases and null value bug - #12997

Merged
auto-submit[bot] merged 6 commits into
flutter:mainfrom
tarrinneal:check-for-bools-in-swift-numbers
Sep 24, 2026
Merged

auto-submit[bot] merged 6 commits into
flutter:mainfrom
tarrinneal:check-for-bools-in-swift-numbers

Conversation

@tarrinneal

Copy link
Copy Markdown
Contributor

Fixes flutter/flutter#193266
Fixes two Swift FFI value serialization bugs in SwiftGenerator:

  1. CFBoolean and floating-point NSNumber bridging in wrapNumber: When an Objective-C boolean (__NSCFBoolean / CFBoolean) or a whole-number floating-point NSNumber (such as 3.0 returned from UserDefaults.object(forKey:) or NSDictionary) is boxed inside Any, Swift's dynamic as? Int cast succeeds before reaching case let value as Double: or case let value as Bool:. wrapNumber now checks CFGetTypeID(number as CFTypeRef) == CFBooleanGetTypeID() (type: 3) and CFNumberIsFloatType(nsNumber) (type: 2) prior to switching on numeric types, matching FlutterStandardCodec behavior.
  2. Dictionary value isNullish check in writeValue: Fixes a typo in writeValue where dictionary serialization checked isNullish(key) instead of isNullish(value) when serializing dictionary values.

Fixes https://github.com/flutter/flutter/issues/<ISSUE_NUMBER>

Pre-Review Checklist

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-assist bot 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

  1. 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

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

Copy link
Copy Markdown
Contributor

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 bumps the Pigeon version to 29.0.4 and introduces fixes for Swift FFI number wrapping and dictionary serialization. Specifically, it corrects dictionary serialization to check isNullish on the value rather than the key, and updates wrapNumber to check CFBooleanGetTypeID and CFNumberIsFloatType before numeric casts. Feedback suggests safely casting number to NSNumber before calling CFGetTypeID to avoid potential runtime crashes with Swift value types, and updating the corresponding test expectations.

Comment on lines +2324 to +2331
indent.format('''
if CFGetTypeID(number as CFTypeRef) == CFBooleanGetTypeID(), let value = number as? Bool {
return ${_classNamePrefix}NumberWrapper(number: NSNumber(value: value), type: 3)
}
if let nsNumber = number as? NSNumber, CFNumberIsFloatType(nsNumber) {
return ${_classNamePrefix}NumberWrapper(number: nsNumber, type: 2)
}
''');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Directly casting number as CFTypeRef can lead to runtime crashes if number is a Swift value type (like a custom struct or enum) that cannot be bridged to an Objective-C class, as it gets boxed in _SwiftValue which does not support CoreFoundation runtime type queries. Safely casting number to NSNumber first ensures that CFGetTypeID is only called on valid CoreFoundation objects.

        indent.format('''
  if let nsNumber = number as? NSNumber {
    if CFGetTypeID(nsNumber as CFTypeRef) == CFBooleanGetTypeID(), let value = number as? Bool {
      return ${_classNamePrefix}NumberWrapper(number: NSNumber(value: value), type: 3)
    }
    if CFNumberIsFloatType(nsNumber) {
      return ${_classNamePrefix}NumberWrapper(number: nsNumber, type: 2)
    }
  }
''');

Comment on lines +1825 to +1835
expect(
code,
contains(
'if CFGetTypeID(number as CFTypeRef) == CFBooleanGetTypeID(), let value = number as? Bool {\n'
' return NumberWrapper(number: NSNumber(value: value), type: 3)\n'
' }\n'
' if let nsNumber = number as? NSNumber, CFNumberIsFloatType(nsNumber) {\n'
' return NumberWrapper(number: nsNumber, type: 2)\n'
' }',
),
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Update the test expectation to match the safer wrapNumber implementation that checks if number is an NSNumber before calling CFGetTypeID.

Suggested change
expect(
code,
contains(
'if CFGetTypeID(number as CFTypeRef) == CFBooleanGetTypeID(), let value = number as? Bool {\n'
' return NumberWrapper(number: NSNumber(value: value), type: 3)\n'
' }\n'
' if let nsNumber = number as? NSNumber, CFNumberIsFloatType(nsNumber) {\n'
' return NumberWrapper(number: nsNumber, type: 2)\n'
' }',
),
);
expect(
code,
contains(
' if let nsNumber = number as? NSNumber {\n'
' if CFGetTypeID(nsNumber as CFTypeRef) == CFBooleanGetTypeID(), let value = number as? Bool {\n'
' return NumberWrapper(number: NSNumber(value: value), type: 3)\n'
' }\n'
' if CFNumberIsFloatType(nsNumber) {\n'
' return NumberWrapper(number: nsNumber, type: 2)\n'
' }\n'
' }',
),
);

@stuartmorgan-g stuartmorgan-g left a comment

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.

LGTM with one question, but I would really recommend an integration test for this.

}
if CFNumberIsFloatType(nsNumber) {
return NativeInteropExampleNumberWrapper(number: nsNumber, type: 2)
}

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.

Why don't we need int-as-NSNumber handling here too?

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 as int check works for that

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.

If the as checks work, then why do we need this code at all vs. just changing the evaluation order of the existing checks?

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 issue is that ordering the switch block to handle all cases isn't possible. The bool and float checks make it so all other cases that succeed into as int work correctly.

Comment thread packages/pigeon/test/swift_generator_test.dart Outdated
@tarrinneal tarrinneal added the autosubmit Merge PR when tree becomes green via auto submit App label Sep 24, 2026
@auto-submit auto-submit Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Sep 24, 2026
@auto-submit

auto-submit Bot commented Sep 24, 2026

Copy link
Copy Markdown
Contributor

autosubmit label was removed for flutter/packages/12997, because - The status or check suite Dashboard Checks has failed. Please fix the issues identified (or deflake) before re-applying this label.

@tarrinneal tarrinneal added the autosubmit Merge PR when tree becomes green via auto submit App label Sep 24, 2026
@auto-submit
auto-submit Bot merged commit 40b7e64 into flutter:main Sep 24, 2026
13 checks passed
pull Bot pushed a commit to fucheng-guo-sun/flutter that referenced this pull request Sep 25, 2026
…r#193351)

flutter/packages@431ea69...e55e7ac

2026-09-25 [email protected] [material_ui] Add
labelTextDirection handling in InputDecorator (flutter/packages#12607)
2026-09-24 [email protected] [pigeon] Fix NSnumber edge cases and
null value bug (flutter/packages#12997)
2026-09-24 [email protected] [camera] add
custom path ouput to recording (flutter/packages#11774)
2026-09-24 [email protected] [Android 17] Update packages CI test
runners to SDK 37 (flutter/packages#12376)
2026-09-24 [email protected]
[pigeon][video_player] Disable `video_player` and `pigeon` flakes
blocking latest flutter -> packages roll (flutter/packages#13003)
2026-09-24 [email protected]
[google_maps_flutter] Add onPointOfInterestTap platform implementations
(flutter/packages#12880)

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

autosubmit Merge PR when tree becomes green via auto submit App CICD Run CI/CD p: pigeon platform-ios platform-macos

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[pigeon] Swift FFI wrapNumber serializes Objective-C CFBoolean / __NSCFBoolean inside Any as int instead of bool

2 participants