From 62dab9ea3119d65fb00ba69e505987486bd897aa Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Tue, 18 Nov 2025 22:45:30 -0500 Subject: [PATCH 1/4] catch error --- packages/pigeon/CHANGELOG.md | 4 ++++ packages/pigeon/lib/src/generator_tools.dart | 2 +- .../pigeon/lib/src/swift/swift_generator.dart | 2 +- packages/pigeon/lib/src/swift/templates.dart | 10 ++++++++-- .../Sources/test_plugin/ProxyApiTests.gen.swift | 12 +++++++++--- .../ios/RunnerTests/InstanceManagerTests.swift | 17 +++++++++++++++++ packages/pigeon/pubspec.yaml | 2 +- 7 files changed, 41 insertions(+), 8 deletions(-) diff --git a/packages/pigeon/CHANGELOG.md b/packages/pigeon/CHANGELOG.md index 8eea8378b28..977c08f987c 100644 --- a/packages/pigeon/CHANGELOG.md +++ b/packages/pigeon/CHANGELOG.md @@ -1,3 +1,7 @@ +## 26.1.2 + +* [swift] Adds a do/catch when a deallocated object calls back to Dart to prevent a crash. + ## 26.1.1 * Updates supported `analyzer` versions to 8.x or 9.x. diff --git a/packages/pigeon/lib/src/generator_tools.dart b/packages/pigeon/lib/src/generator_tools.dart index e75bdfcd1ae..2b687eb632d 100644 --- a/packages/pigeon/lib/src/generator_tools.dart +++ b/packages/pigeon/lib/src/generator_tools.dart @@ -15,7 +15,7 @@ import 'generator.dart'; /// The current version of pigeon. /// /// This must match the version in pubspec.yaml. -const String pigeonVersion = '26.1.1'; +const String pigeonVersion = '26.1.2'; /// Read all the content from [stdin] to a String. String readStdin() { diff --git a/packages/pigeon/lib/src/swift/swift_generator.dart b/packages/pigeon/lib/src/swift/swift_generator.dart index 2f30a35fd4e..93281a19c81 100644 --- a/packages/pigeon/lib/src/swift/swift_generator.dart +++ b/packages/pigeon/lib/src/swift/swift_generator.dart @@ -1997,7 +1997,7 @@ func deepHash${generatorOptions.fileSpecificClassNameComponent}(value: Any?, has self.api = api } - public func onDeinit(identifier: Int64) { + public func onDeinit(identifier: Int64) throws { api.removeStrongReference(identifier: identifier) { _ in } diff --git a/packages/pigeon/lib/src/swift/templates.dart b/packages/pigeon/lib/src/swift/templates.dart index 35266a2944c..2d1c8c820cf 100644 --- a/packages/pigeon/lib/src/swift/templates.dart +++ b/packages/pigeon/lib/src/swift/templates.dart @@ -28,7 +28,7 @@ String instanceManagerFinalizerDelegateTemplate(InternalSwiftOptions options) => /// Handles the callback when an object is deallocated. protocol ${instanceManagerFinalizerDelegateName(options)}: AnyObject { /// Invoked when the strong reference of an object is deallocated in an `InstanceManager`. - func onDeinit(identifier: Int64) + func onDeinit(identifier: Int64) throws } '''; @@ -66,7 +66,13 @@ internal final class ${_instanceManagerFinalizerName(options)} { } deinit { - delegate?.onDeinit(identifier: identifier) + do { + try delegate?.onDeinit(identifier: identifier) + } catch { + NSLog( + "Failed to call `onDeinit` on object with identifier: \\(identifier)\\n\\(error)\\nStacktrace: \\(Thread.callStackSymbols)" + ) + } } } diff --git a/packages/pigeon/platform_tests/test_plugin/darwin/test_plugin/Sources/test_plugin/ProxyApiTests.gen.swift b/packages/pigeon/platform_tests/test_plugin/darwin/test_plugin/Sources/test_plugin/ProxyApiTests.gen.swift index 56de32c5cf1..27fa26db0aa 100644 --- a/packages/pigeon/platform_tests/test_plugin/darwin/test_plugin/Sources/test_plugin/ProxyApiTests.gen.swift +++ b/packages/pigeon/platform_tests/test_plugin/darwin/test_plugin/Sources/test_plugin/ProxyApiTests.gen.swift @@ -77,7 +77,7 @@ private func nilOrValue(_ value: Any?) -> T? { /// Handles the callback when an object is deallocated. protocol ProxyApiTestsPigeonInternalFinalizerDelegate: AnyObject { /// Invoked when the strong reference of an object is deallocated in an `InstanceManager`. - func onDeinit(identifier: Int64) + func onDeinit(identifier: Int64) throws } // Attaches to an object to receive a callback when the object is deallocated. @@ -113,7 +113,13 @@ internal final class ProxyApiTestsPigeonInternalFinalizer { } deinit { - delegate?.onDeinit(identifier: identifier) + do { + try delegate?.onDeinit(identifier: identifier) + } catch { + NSLog( + "Failed to call `onDeinit` on object with identifier: \(identifier)\n\(error)\nStacktrace: \(Thread.callStackSymbols)" + ) + } } } @@ -428,7 +434,7 @@ open class ProxyApiTestsPigeonProxyApiRegistrar { self.api = api } - public func onDeinit(identifier: Int64) { + public func onDeinit(identifier: Int64) throws { api.removeStrongReference(identifier: identifier) { _ in } diff --git a/packages/pigeon/platform_tests/test_plugin/example/ios/RunnerTests/InstanceManagerTests.swift b/packages/pigeon/platform_tests/test_plugin/example/ios/RunnerTests/InstanceManagerTests.swift index 716360ed50b..a502d584326 100644 --- a/packages/pigeon/platform_tests/test_plugin/example/ios/RunnerTests/InstanceManagerTests.swift +++ b/packages/pigeon/platform_tests/test_plugin/example/ios/RunnerTests/InstanceManagerTests.swift @@ -147,6 +147,17 @@ final class InstanceManagerTests: XCTestCase { XCTAssertNil( objc_getAssociatedObject(object!, ProxyApiTestsPigeonInternalFinalizer.associatedObjectKey)) } + + func testSomething() { + let finalizerDelegate = ThrowingFinalizerDelegate() + + var object: NSObject? = NSObject() + ProxyApiTestsPigeonInternalFinalizer.attach( + to: object!, identifier: 0, delegate: finalizerDelegate) + + //object = nil + XCTAssertNoThrow(object = nil) + } } class EmptyFinalizerDelegate: ProxyApiTestsPigeonInternalFinalizerDelegate { @@ -160,3 +171,9 @@ class TestFinalizerDelegate: ProxyApiTestsPigeonInternalFinalizerDelegate { lastHandledIdentifier = identifier } } + +class ThrowingFinalizerDelegate: ProxyApiTestsPigeonInternalFinalizerDelegate { + func onDeinit(identifier: Int64) throws { + fatalError() + } +} diff --git a/packages/pigeon/pubspec.yaml b/packages/pigeon/pubspec.yaml index 44fa849c60c..6298399391e 100644 --- a/packages/pigeon/pubspec.yaml +++ b/packages/pigeon/pubspec.yaml @@ -2,7 +2,7 @@ name: pigeon description: Code generator tool to make communication between Flutter and the host platform type-safe and easier. repository: https://github.com/flutter/packages/tree/main/packages/pigeon issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+pigeon%22 -version: 26.1.1 # This must match the version in lib/src/generator_tools.dart +version: 26.1.2 # This must match the version in lib/src/generator_tools.dart environment: sdk: ^3.8.0 From 9a6e49dd5b473a799ddcd9be73cbc28d35c38eff Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Tue, 18 Nov 2025 22:46:12 -0500 Subject: [PATCH 2/4] formatting --- .../example/ios/RunnerTests/InstanceManagerTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pigeon/platform_tests/test_plugin/example/ios/RunnerTests/InstanceManagerTests.swift b/packages/pigeon/platform_tests/test_plugin/example/ios/RunnerTests/InstanceManagerTests.swift index a502d584326..b3e970bd3a1 100644 --- a/packages/pigeon/platform_tests/test_plugin/example/ios/RunnerTests/InstanceManagerTests.swift +++ b/packages/pigeon/platform_tests/test_plugin/example/ios/RunnerTests/InstanceManagerTests.swift @@ -147,7 +147,7 @@ final class InstanceManagerTests: XCTestCase { XCTAssertNil( objc_getAssociatedObject(object!, ProxyApiTestsPigeonInternalFinalizer.associatedObjectKey)) } - + func testSomething() { let finalizerDelegate = ThrowingFinalizerDelegate() From 90960e9fdd56fbbe8b798ed567797380cc0f7549 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Tue, 18 Nov 2025 22:47:47 -0500 Subject: [PATCH 3/4] rename test --- .../example/ios/RunnerTests/InstanceManagerTests.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/pigeon/platform_tests/test_plugin/example/ios/RunnerTests/InstanceManagerTests.swift b/packages/pigeon/platform_tests/test_plugin/example/ios/RunnerTests/InstanceManagerTests.swift index b3e970bd3a1..955af725c9c 100644 --- a/packages/pigeon/platform_tests/test_plugin/example/ios/RunnerTests/InstanceManagerTests.swift +++ b/packages/pigeon/platform_tests/test_plugin/example/ios/RunnerTests/InstanceManagerTests.swift @@ -148,14 +148,13 @@ final class InstanceManagerTests: XCTestCase { objc_getAssociatedObject(object!, ProxyApiTestsPigeonInternalFinalizer.associatedObjectKey)) } - func testSomething() { + func testExceptionInDeinitDoesNotCauseCrash() { let finalizerDelegate = ThrowingFinalizerDelegate() var object: NSObject? = NSObject() ProxyApiTestsPigeonInternalFinalizer.attach( to: object!, identifier: 0, delegate: finalizerDelegate) - //object = nil XCTAssertNoThrow(object = nil) } } From 7bb2b84825a94120b6f3bd6f02e543455805cf49 Mon Sep 17 00:00:00 2001 From: Maurice Parrish <10687576+bparrishMines@users.noreply.github.com> Date: Tue, 18 Nov 2025 22:49:50 -0500 Subject: [PATCH 4/4] also verify it was called --- .../example/ios/RunnerTests/InstanceManagerTests.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/pigeon/platform_tests/test_plugin/example/ios/RunnerTests/InstanceManagerTests.swift b/packages/pigeon/platform_tests/test_plugin/example/ios/RunnerTests/InstanceManagerTests.swift index 955af725c9c..75b4fafafce 100644 --- a/packages/pigeon/platform_tests/test_plugin/example/ios/RunnerTests/InstanceManagerTests.swift +++ b/packages/pigeon/platform_tests/test_plugin/example/ios/RunnerTests/InstanceManagerTests.swift @@ -156,6 +156,7 @@ final class InstanceManagerTests: XCTestCase { to: object!, identifier: 0, delegate: finalizerDelegate) XCTAssertNoThrow(object = nil) + XCTAssertTrue(finalizerDelegate.deinitCalled) } } @@ -172,7 +173,10 @@ class TestFinalizerDelegate: ProxyApiTestsPigeonInternalFinalizerDelegate { } class ThrowingFinalizerDelegate: ProxyApiTestsPigeonInternalFinalizerDelegate { + var deinitCalled = false + func onDeinit(identifier: Int64) throws { + deinitCalled = true fatalError() } }