|
2 | 2 | //
|
3 | 3 | // This source file is part of the Swift.org open source project
|
4 | 4 | //
|
5 |
| -// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors |
| 5 | +// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors |
6 | 6 | // Licensed under Apache License v2.0 with Runtime Library Exception
|
7 | 7 | //
|
8 | 8 | // See https://swift.org/LICENSE.txt for license information
|
@@ -417,6 +417,87 @@ extension _StringGuts {
|
417 | 417 | }
|
418 | 418 | }
|
419 | 419 |
|
| 420 | +#if _runtime(_ObjC) |
| 421 | +extension _StringGuts { |
| 422 | + |
| 423 | + private static var associationKey: UnsafeRawPointer { |
| 424 | + // We never dereference this, we just use this address as a unique key |
| 425 | + // TODO: perhaps use the address of a metatype instead |
| 426 | + unsafe UnsafeRawPointer(Builtin.addressof(&_swiftEmptyArrayStorage)) |
| 427 | + } |
| 428 | + |
| 429 | + internal func getAssociatedStorage() -> __StringStorage? { |
| 430 | + let getter = unsafe unsafeBitCast( |
| 431 | + getGetAssociatedObjectPtr(), |
| 432 | + to: (@convention(c)( |
| 433 | + AnyObject, |
| 434 | + UnsafeRawPointer |
| 435 | + ) -> UnsafeRawPointer?).self |
| 436 | + ) |
| 437 | + _precondition(_object.hasObjCBridgeableObject) |
| 438 | + // print("has ObjC Bridgeable Object") |
| 439 | + if let assocPtr = unsafe getter( |
| 440 | + _object.objCBridgeableObject, |
| 441 | + Self.associationKey |
| 442 | + ) { |
| 443 | + let storage: __StringStorage |
| 444 | + storage = unsafe Unmanaged.fromOpaque(assocPtr).takeUnretainedValue() |
| 445 | + return storage |
| 446 | + } |
| 447 | + return nil |
| 448 | + } |
| 449 | + |
| 450 | + internal func setAssociatedStorage(_ storage: __StringStorage) { |
| 451 | + let setter = unsafe unsafeBitCast( |
| 452 | + getSetAssociatedObjectPtr(), |
| 453 | + to: (@convention(c)( |
| 454 | + AnyObject, |
| 455 | + UnsafeRawPointer, |
| 456 | + AnyObject?, |
| 457 | + UInt |
| 458 | + ) -> Void).self |
| 459 | + ) |
| 460 | + |
| 461 | + unsafe setter( |
| 462 | + _object.objCBridgeableObject, |
| 463 | + Self.associationKey, |
| 464 | + storage, |
| 465 | + 1 //OBJC_ASSOCIATION_RETAIN_NONATOMIC |
| 466 | + ) |
| 467 | + } |
| 468 | + |
| 469 | + internal func getOrAllocateAssociatedStorage() -> __StringStorage { |
| 470 | + _precondition(_object.hasObjCBridgeableObject) |
| 471 | + let unwrapped: __StringStorage |
| 472 | + // libobjc already provides the necessary memory barriers for |
| 473 | + // double checked locking to be safe, per comments on |
| 474 | + // https://github.com/swiftlang/swift/pull/75148 |
| 475 | + if let storage = getAssociatedStorage() { |
| 476 | + unwrapped = storage |
| 477 | + } else { |
| 478 | + let lock = _object.objCBridgeableObject |
| 479 | + objc_sync_enter(lock) |
| 480 | + if let storage = getAssociatedStorage() { |
| 481 | + unwrapped = storage |
| 482 | + } else { |
| 483 | + var contents = String.UnicodeScalarView() |
| 484 | + // always reserve a size larger than a small string |
| 485 | + contents.reserveCapacity(Swift.max(31, 1 + count + count >> 1)) |
| 486 | + for c in String.UnicodeScalarView(self) { |
| 487 | + contents.append(c) |
| 488 | + } |
| 489 | + _precondition(contents._guts._object.hasNativeStorage) |
| 490 | + unwrapped = (consume contents)._guts._object.nativeStorage |
| 491 | + setAssociatedStorage(unwrapped) |
| 492 | + } |
| 493 | + defer { _fixLifetime(unwrapped) } |
| 494 | + objc_sync_exit(lock) |
| 495 | + } |
| 496 | + return unwrapped |
| 497 | + } |
| 498 | +} |
| 499 | +#endif |
| 500 | + |
420 | 501 | // Old SPI(corelibs-foundation)
|
421 | 502 | extension _StringGuts {
|
422 | 503 | public // SPI(corelibs-foundation)
|
|
0 commit comments