mirror of
https://github.com/apple/swift-foundation.git
synced 2025-05-21 21:09:57 +08:00
Use dynamic replacement instead of _typeByName for internationalization upcalls (#756)
* Use dynamic replacement instead of _typeByName for internationalization upcalls * Make FOUNDATION_FRAMEWORK function non-dynamic * Fix build failures
This commit is contained in:
parent
27439d1ecf
commit
196376b5fd
@ -15,30 +15,26 @@ internal import _ForSwiftFoundation
|
||||
import CoreFoundation
|
||||
#endif
|
||||
|
||||
#if FOUNDATION_FRAMEWORK && canImport(_FoundationICU)
|
||||
internal func _calendarICUClass() -> _CalendarProtocol.Type? {
|
||||
_CalendarICU.self
|
||||
}
|
||||
#else
|
||||
dynamic package func _calendarICUClass() -> _CalendarProtocol.Type? {
|
||||
nil
|
||||
}
|
||||
#endif
|
||||
|
||||
func _calendarClass(identifier: Calendar.Identifier, useGregorian: Bool) -> _CalendarProtocol.Type? {
|
||||
if useGregorian && identifier == .gregorian {
|
||||
return _CalendarGregorian.self
|
||||
} else {
|
||||
return _calendarICUClass()
|
||||
}
|
||||
}
|
||||
|
||||
/// Singleton which listens for notifications about preference changes for Calendar and holds cached singletons for the current locale, calendar, and time zone.
|
||||
struct CalendarCache : Sendable {
|
||||
|
||||
// MARK: - Concrete Classes
|
||||
|
||||
// _CalendarICU, if present
|
||||
static func calendarICUClass(identifier: Calendar.Identifier, useGregorian: Bool) -> _CalendarProtocol.Type? {
|
||||
#if FOUNDATION_FRAMEWORK && canImport(_FoundationICU)
|
||||
if useGregorian && identifier == .gregorian {
|
||||
return _CalendarGregorian.self
|
||||
} else {
|
||||
return _CalendarICU.self
|
||||
}
|
||||
#else
|
||||
if useGregorian && identifier == .gregorian {
|
||||
return _CalendarGregorian.self
|
||||
} else if let name = _typeByName("FoundationInternationalization._CalendarICU"), let t = name as? _CalendarProtocol.Type {
|
||||
return t
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// MARK: - State
|
||||
|
||||
struct State : Sendable {
|
||||
@ -78,7 +74,7 @@ struct CalendarCache : Sendable {
|
||||
} else {
|
||||
let id = Locale.current._calendarIdentifier
|
||||
// If we cannot create the right kind of class, we fail immediately here
|
||||
let calendarClass = CalendarCache.calendarICUClass(identifier: id, useGregorian: true)!
|
||||
let calendarClass = _calendarClass(identifier: id, useGregorian: true)!
|
||||
let calendar = calendarClass.init(identifier: id, timeZone: nil, locale: Locale.current, firstWeekday: nil, minimumDaysInFirstWeek: nil, gregorianStartDate: nil)
|
||||
currentCalendar = calendar
|
||||
return calendar
|
||||
@ -101,7 +97,7 @@ struct CalendarCache : Sendable {
|
||||
return cached
|
||||
} else {
|
||||
// If we cannot create the right kind of class, we fail immediately here
|
||||
let calendarClass = CalendarCache.calendarICUClass(identifier: id, useGregorian: true)!
|
||||
let calendarClass = _calendarClass(identifier: id, useGregorian: true)!
|
||||
let new = calendarClass.init(identifier: id, timeZone: nil, locale: nil, firstWeekday: nil, minimumDaysInFirstWeek: nil, gregorianStartDate: nil)
|
||||
fixedCalendars[id] = new
|
||||
return new
|
||||
@ -140,7 +136,7 @@ struct CalendarCache : Sendable {
|
||||
func fixed(identifier: Calendar.Identifier, locale: Locale?, timeZone: TimeZone?, firstWeekday: Int?, minimumDaysInFirstWeek: Int?, gregorianStartDate: Date?) -> any _CalendarProtocol {
|
||||
// Note: Only the ObjC NSCalendar initWithCoder supports gregorian start date values. For Swift it is always nil.
|
||||
// If we cannot create the right kind of class, we fail immediately here
|
||||
let calendarClass = CalendarCache.calendarICUClass(identifier: identifier, useGregorian: true)!
|
||||
let calendarClass = _calendarClass(identifier: identifier, useGregorian: true)!
|
||||
return calendarClass.init(identifier: identifier, timeZone: timeZone, locale: locale, firstWeekday: firstWeekday, minimumDaysInFirstWeek: minimumDaysInFirstWeek, gregorianStartDate: gregorianStartDate)
|
||||
}
|
||||
|
||||
|
@ -118,9 +118,11 @@ public protocol _NSNumberInitializer {
|
||||
static func initialize(value: some BinaryInteger) -> Any
|
||||
}
|
||||
|
||||
private let _nsNumberInitializer: (any _NSNumberInitializer.Type)? = {
|
||||
@_spi(SwiftCorelibsFoundation)
|
||||
dynamic public func _nsNumberInitializer() -> (any _NSNumberInitializer.Type)? {
|
||||
// TODO: return nil here after swift-corelibs-foundation begins dynamically replacing this function
|
||||
_typeByName("Foundation._FoundationNSNumberInitializer") as? any _NSNumberInitializer.Type
|
||||
}()
|
||||
}
|
||||
#endif
|
||||
|
||||
func _writeFileAttributePrimitive<T: BinaryInteger, U: BinaryInteger>(_ value: T, as type: U.Type) -> Any {
|
||||
@ -131,7 +133,7 @@ func _writeFileAttributePrimitive<T: BinaryInteger, U: BinaryInteger>(_ value: T
|
||||
NSNumber(value: UInt64(value))
|
||||
}
|
||||
#else
|
||||
if let ns = _nsNumberInitializer?.initialize(value: value) {
|
||||
if let ns = _nsNumberInitializer()?.initialize(value: value) {
|
||||
return ns
|
||||
} else {
|
||||
return U(value)
|
||||
@ -143,7 +145,7 @@ func _writeFileAttributePrimitive(_ value: Bool) -> Any {
|
||||
#if FOUNDATION_FRAMEWORK
|
||||
NSNumber(value: value)
|
||||
#else
|
||||
if let ns = _nsNumberInitializer?.initialize(value: value) {
|
||||
if let ns = _nsNumberInitializer()?.initialize(value: value) {
|
||||
return ns
|
||||
} else {
|
||||
return value
|
||||
|
@ -19,23 +19,20 @@ internal import os
|
||||
|
||||
internal import _FoundationCShims
|
||||
|
||||
#if FOUNDATION_FRAMEWORK && canImport(_FoundationICU)
|
||||
// Here, we always have access to _LocaleICU
|
||||
internal func _localeICUClass() -> _LocaleProtocol.Type {
|
||||
_LocaleICU.self
|
||||
}
|
||||
#else
|
||||
dynamic package func _localeICUClass() -> _LocaleProtocol.Type {
|
||||
// Return _LocaleUnlocalized if FoundationInternationalization isn't loaded. The `Locale` initializers are not failable, so we just fall back to the unlocalized type when needed without failure.
|
||||
_LocaleUnlocalized.self
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Singleton which listens for notifications about preference changes for Locale and holds cached singletons.
|
||||
struct LocaleCache : Sendable {
|
||||
// MARK: - Concrete Classes
|
||||
|
||||
// _LocaleICU, if present. Otherwise we use _LocaleUnlocalized. The `Locale` initializers are not failable, so we just fall back to the unlocalized type when needed without failure.
|
||||
static let localeICUClass: _LocaleProtocol.Type = {
|
||||
#if FOUNDATION_FRAMEWORK && canImport(_FoundationICU)
|
||||
return _LocaleICU.self
|
||||
#else
|
||||
if let name = _typeByName("FoundationInternationalization._LocaleICU"), let t = name as? _LocaleProtocol.Type {
|
||||
return t
|
||||
} else {
|
||||
return _LocaleUnlocalized.self
|
||||
}
|
||||
#endif
|
||||
}()
|
||||
|
||||
// MARK: - State
|
||||
|
||||
struct State {
|
||||
@ -99,7 +96,7 @@ struct LocaleCache : Sendable {
|
||||
return nil
|
||||
}
|
||||
|
||||
let locale = LocaleCache.localeICUClass.init(name: nil, prefs: preferences, disableBundleMatching: disableBundleMatching)
|
||||
let locale = _localeICUClass().init(name: nil, prefs: preferences, disableBundleMatching: disableBundleMatching)
|
||||
if cache {
|
||||
// It's possible this was an 'incomplete locale', in which case we will want to calculate it again later.
|
||||
self.cachedCurrentLocale = locale
|
||||
@ -122,7 +119,7 @@ struct LocaleCache : Sendable {
|
||||
if let locale = cachedFixedLocales[id] {
|
||||
return locale
|
||||
} else {
|
||||
let locale = LocaleCache.localeICUClass.init(identifier: id, prefs: nil)
|
||||
let locale = _localeICUClass().init(identifier: id, prefs: nil)
|
||||
cachedFixedLocales[id] = locale
|
||||
return locale
|
||||
}
|
||||
@ -217,7 +214,7 @@ struct LocaleCache : Sendable {
|
||||
if let l = cachedFixedComponentsLocales[comps] {
|
||||
return l
|
||||
} else {
|
||||
let new = LocaleCache.localeICUClass.init(components: comps)
|
||||
let new = _localeICUClass().init(components: comps)
|
||||
|
||||
cachedFixedComponentsLocales[comps] = new
|
||||
return new
|
||||
@ -229,7 +226,7 @@ struct LocaleCache : Sendable {
|
||||
return locale
|
||||
}
|
||||
|
||||
let locale = LocaleCache.localeICUClass.init(identifier: "", prefs: nil)
|
||||
let locale = _localeICUClass().init(identifier: "", prefs: nil)
|
||||
cachedSystemLocale = locale
|
||||
return locale
|
||||
}
|
||||
@ -415,13 +412,13 @@ struct LocaleCache : Sendable {
|
||||
var (prefs, _) = preferences()
|
||||
if let overrides { prefs.apply(overrides) }
|
||||
|
||||
let inner = LocaleCache.localeICUClass.init(name: name, prefs: prefs, disableBundleMatching: disableBundleMatching)
|
||||
let inner = _localeICUClass().init(name: name, prefs: prefs, disableBundleMatching: disableBundleMatching)
|
||||
return Locale(inner: inner)
|
||||
}
|
||||
|
||||
func localeWithPreferences(identifier: String, prefs: LocalePreferences?) -> Locale {
|
||||
if let prefs {
|
||||
let inner = LocaleCache.localeICUClass.init(identifier: identifier, prefs: prefs)
|
||||
let inner = _localeICUClass().init(identifier: identifier, prefs: prefs)
|
||||
return Locale(inner: inner)
|
||||
} else {
|
||||
return Locale(inner: LocaleCache.cache.fixed(identifier))
|
||||
|
@ -31,37 +31,25 @@ internal import _ForSwiftFoundation
|
||||
internal import CoreFoundation_Private.CFNotificationCenter
|
||||
#endif
|
||||
|
||||
|
||||
#if FOUNDATION_FRAMEWORK && canImport(_FoundationICU)
|
||||
internal func _timeZoneICUClass() -> _TimeZoneProtocol.Type? {
|
||||
_TimeZoneICU.self
|
||||
}
|
||||
internal func _timeZoneGMTClass() -> _TimeZoneProtocol.Type {
|
||||
_TimeZoneGMTICU.self
|
||||
}
|
||||
#else
|
||||
dynamic package func _timeZoneICUClass() -> _TimeZoneProtocol.Type? {
|
||||
nil
|
||||
}
|
||||
dynamic package func _timeZoneGMTClass() -> _TimeZoneProtocol.Type {
|
||||
_TimeZoneGMT.self
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Singleton which listens for notifications about preference changes for TimeZone and holds cached values for current, fixed time zones, etc.
|
||||
struct TimeZoneCache : Sendable {
|
||||
|
||||
// MARK: - Concrete Classes
|
||||
|
||||
// _TimeZoneICU, if present
|
||||
static let timeZoneICUClass: _TimeZoneProtocol.Type? = {
|
||||
#if FOUNDATION_FRAMEWORK && canImport(_FoundationICU)
|
||||
_TimeZoneICU.self
|
||||
#else
|
||||
if let name = _typeByName("FoundationInternationalization._TimeZoneICU"), let t = name as? _TimeZoneProtocol.Type {
|
||||
return t
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
#endif
|
||||
}()
|
||||
|
||||
// _TimeZoneGMTICU or _TimeZoneGMT
|
||||
static let timeZoneGMTClass: _TimeZoneProtocol.Type = {
|
||||
#if FOUNDATION_FRAMEWORK && canImport(_FoundationICU)
|
||||
_TimeZoneGMTICU.self
|
||||
#else
|
||||
if let name = _typeByName("FoundationInternationalization._TimeZoneGMTICU"), let t = name as? _TimeZoneProtocol.Type {
|
||||
return t
|
||||
} else {
|
||||
return _TimeZoneGMT.self
|
||||
}
|
||||
#endif
|
||||
}()
|
||||
|
||||
// MARK: - State
|
||||
|
||||
struct State {
|
||||
@ -239,7 +227,7 @@ struct TimeZoneCache : Sendable {
|
||||
} else if let cached = fixedTimeZones[identifier] {
|
||||
return cached
|
||||
} else {
|
||||
if let innerTz = TimeZoneCache.timeZoneICUClass?.init(identifier: identifier) {
|
||||
if let innerTz = _timeZoneICUClass()?.init(identifier: identifier) {
|
||||
fixedTimeZones[identifier] = innerTz
|
||||
return innerTz
|
||||
} else {
|
||||
@ -254,7 +242,7 @@ struct TimeZoneCache : Sendable {
|
||||
} else {
|
||||
// In order to avoid bloating a cache with weird time zones, only cache values that are 30min offsets (including 1hr offsets).
|
||||
let doCache = abs(offset) % 1800 == 0
|
||||
if let innerTz = TimeZoneCache.timeZoneGMTClass.init(secondsFromGMT: offset) {
|
||||
if let innerTz = _timeZoneGMTClass().init(secondsFromGMT: offset) {
|
||||
if doCache {
|
||||
offsetTimeZones[offset] = innerTz
|
||||
}
|
||||
|
@ -158,17 +158,18 @@ package protocol UIDNAHook {
|
||||
static func decode(_ host: some StringProtocol) -> String?
|
||||
}
|
||||
|
||||
#if FOUNDATION_FRAMEWORK && canImport(_FoundationICU)
|
||||
internal func _uidnaHook() -> UIDNAHook.Type? {
|
||||
UIDNAHookICU.self
|
||||
}
|
||||
#else
|
||||
dynamic package func _uidnaHook() -> UIDNAHook.Type? {
|
||||
nil
|
||||
}
|
||||
#endif
|
||||
|
||||
internal struct RFC3986Parser: URLParserProtocol {
|
||||
static let kind: URLParserKind = .RFC3986
|
||||
static let uidnaHook: UIDNAHook.Type? = {
|
||||
#if FOUNDATION_FRAMEWORK && !canImport(_FoundationICU)
|
||||
nil
|
||||
#elseif FOUNDATION_FRAMEWORK
|
||||
UIDNAHookICU.self
|
||||
#else
|
||||
_typeByName("FoundationInternationalization.UIDNAHookICU") as? UIDNAHook.Type
|
||||
#endif
|
||||
}()
|
||||
|
||||
// MARK: - Encoding
|
||||
|
||||
@ -233,7 +234,7 @@ internal struct RFC3986Parser: URLParserProtocol {
|
||||
}
|
||||
|
||||
static func shouldPercentEncodeHost(_ host: some StringProtocol, forScheme scheme: (some StringProtocol)?) -> Bool {
|
||||
guard uidnaHook != nil else {
|
||||
guard _uidnaHook() != nil else {
|
||||
// Always percent-encode the host if we can't access UIDNA encoding functions
|
||||
return true
|
||||
}
|
||||
@ -279,13 +280,13 @@ internal struct RFC3986Parser: URLParserProtocol {
|
||||
static func IDNAEncodeHost(_ host: (some StringProtocol)?) -> String? {
|
||||
guard let host else { return nil }
|
||||
guard !host.isEmpty else { return "" }
|
||||
return uidnaHook?.encode(host)
|
||||
return _uidnaHook()?.encode(host)
|
||||
}
|
||||
|
||||
static func IDNADecodeHost(_ host: (some StringProtocol)?) -> String? {
|
||||
guard let host else { return nil }
|
||||
guard !host.isEmpty else { return "" }
|
||||
guard let uidnaHook else { return String(host) }
|
||||
guard let uidnaHook = _uidnaHook() else { return String(host) }
|
||||
return uidnaHook.decode(host)
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,13 @@ import Darwin
|
||||
|
||||
internal import _FoundationICU
|
||||
|
||||
#if !FOUNDATION_FRAMEWORK
|
||||
@_dynamicReplacement(for: _calendarICUClass())
|
||||
private func _calendarICUClass_localized() -> _CalendarProtocol.Type? {
|
||||
return _CalendarICU.self
|
||||
}
|
||||
#endif
|
||||
|
||||
internal final class _CalendarICU: _CalendarProtocol, @unchecked Sendable {
|
||||
let lock: LockedState<Void>
|
||||
let identifier: Calendar.Identifier
|
||||
|
@ -27,6 +27,13 @@ internal import _FoundationICU
|
||||
import Glibc
|
||||
#endif
|
||||
|
||||
#if !FOUNDATION_FRAMEWORK
|
||||
@_dynamicReplacement(for: _localeICUClass())
|
||||
private func _localeICUClass_localized() -> any _LocaleProtocol.Type {
|
||||
return _LocaleICU.self
|
||||
}
|
||||
#endif
|
||||
|
||||
let MAX_ICU_NAME_SIZE: Int32 = 1024
|
||||
|
||||
internal final class _LocaleICU: _LocaleProtocol, Sendable {
|
||||
|
@ -16,6 +16,13 @@ import FoundationEssentials
|
||||
|
||||
internal import _FoundationICU
|
||||
|
||||
#if !FOUNDATION_FRAMEWORK
|
||||
@_dynamicReplacement(for: _timeZoneGMTClass())
|
||||
private func _timeZoneGMTClass_localized() -> _TimeZoneProtocol.Type {
|
||||
return _TimeZoneGMTICU.self
|
||||
}
|
||||
#endif
|
||||
|
||||
internal final class _TimeZoneGMTICU : _TimeZoneProtocol, @unchecked Sendable {
|
||||
let offset: Int
|
||||
let name: String
|
||||
|
@ -25,6 +25,13 @@ import ucrt
|
||||
#if canImport(_FoundationICU)
|
||||
internal import _FoundationICU
|
||||
|
||||
#if !FOUNDATION_FRAMEWORK
|
||||
@_dynamicReplacement(for: _timeZoneICUClass())
|
||||
private func _timeZoneICUClass_localized() -> _TimeZoneProtocol.Type? {
|
||||
return _TimeZoneICU.self
|
||||
}
|
||||
#endif
|
||||
|
||||
internal final class _TimeZoneICU: _TimeZoneProtocol, Sendable {
|
||||
init?(secondsFromGMT: Int) {
|
||||
fatalError("Unexpected init")
|
||||
|
@ -15,7 +15,14 @@ import FoundationEssentials
|
||||
|
||||
internal import _FoundationICU
|
||||
|
||||
internal final class UIDNAHookICU: UIDNAHook {
|
||||
#if !FOUNDATION_FRAMEWORK
|
||||
@_dynamicReplacement(for: _uidnaHook())
|
||||
private func _uidnaHook_localized() -> UIDNAHook.Type? {
|
||||
return UIDNAHookICU.self
|
||||
}
|
||||
#endif
|
||||
|
||||
struct UIDNAHookICU: UIDNAHook {
|
||||
// `Sendable` notes: `UIDNA` from ICU is thread safe.
|
||||
struct UIDNAPointer : @unchecked Sendable {
|
||||
init(_ ptr: OpaquePointer?) { self.idnaTranscoder = ptr }
|
||||
|
Loading…
x
Reference in New Issue
Block a user