mirror of
https://github.com/firebase/firebase-ios-sdk
synced 2026-11-11 22:55:59 +05:45
GeminiAPIClient's SessionDelegate and TaskDelegate stored Synchronization.Mutex properties. Swift classes are listed in the Objective-C class list regardless of @available, so enumerating the classes (e.g., with objc_copyClassList, as Realm does) or looking one up by name realized them on iOS 17 and earlier. TaskDelegate's Mutex<ResponseState> required runtime layout, so realizing it called the weakly linked Mutex metadata accessor, which is NULL before iOS 18, and the app crashed. GeminiLanguageModel is only built with Swift 6.4+, so this affected apps built with Xcode 27 even if they didn't use it. - Replace Mutex with LockedValue, an NSLock-based class that mirrors the init and withLock signatures of Mutex. Add a TODO to switch back once the minimum deployment targets always include Mutex, and link the upstream Swift runtime issues (swiftlang/swift#61215 and swiftlang/swift#72657). - Add a FirebaseAILogicUnit test that calls objc_copyClassList. No CI runner has a simulator old enough to catch this crash, so the fix was verified locally on a tvOS 17.5 simulator, before and after. Fixes #17012
39 lines
1.6 KiB
Swift
39 lines
1.6 KiB
Swift
// Copyright 2026 Google LLC
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
import Foundation
|
|
import ObjectiveC
|
|
import Testing
|
|
|
|
/// Tests that the Objective-C runtime can realize every class in the SDK.
|
|
///
|
|
/// The Objective-C class list includes Swift classes regardless of their `@available` annotations,
|
|
/// and `objc_copyClassList` realizes every class in it. Realizing a class crashes if one of its
|
|
/// stored properties has a type that's unavailable on the running OS version and whose layout isn't
|
|
/// known at compile time. Apps and libraries (for example, Realm) call `objc_copyClassList` at
|
|
/// launch, so this crash affects apps that don't use the class.
|
|
struct ObjectiveCClassEnumerationTests {
|
|
@Test
|
|
func enumeratingObjectiveCClassesDoesNotCrash() throws {
|
|
var classCount: UInt32 = 0
|
|
|
|
// Don't read the elements as `AnyClass`; doing so retains each class, and some private root
|
|
// classes (for example, `__NSGenericDeallocHandler`) don't implement `retain`.
|
|
let classList = try #require(objc_copyClassList(&classCount))
|
|
free(UnsafeMutableRawPointer(classList))
|
|
|
|
#expect(classCount > 0)
|
|
}
|
|
}
|