Skip to content

[pull] swiftwasm-release/5.7 from release/5.7 #4938

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions stdlib/public/core/SmallString.swift
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,27 @@ extension _SmallString {
}
self._invariantCheck()
}

@_effects(readonly) // @opaque
internal init?(taggedASCIICocoa cocoa: AnyObject) {
self.init()
var success = true
self.withMutableCapacity {
/*
For regular NSTaggedPointerStrings we will always succeed here, but
tagged NSLocalizedStrings may not fit in a SmallString
*/
if let len = _bridgeTaggedASCII(cocoa, intoUTF8: $0) {
return len
}
success = false
return 0
}
if !success {
return nil
}
self._invariantCheck()
}
}
#endif

Expand Down
84 changes: 78 additions & 6 deletions stdlib/public/core/StringBridge.swift
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,9 @@ internal func _cocoaStringSubscript(
}

@_effects(releasenone)
private func _NSStringCopyUTF8(
private func _NSStringCopyBytes(
_ o: _StringSelectorHolder,
encoding: UInt,
into bufPtr: UnsafeMutableRawBufferPointer
) -> Int? {
let ptr = bufPtr.baseAddress._unsafelyUnwrappedUnchecked
Expand All @@ -179,7 +180,7 @@ private func _NSStringCopyUTF8(
ptr,
maxLength: bufPtr.count,
usedLength: &usedLen,
encoding: _cocoaUTF8Encoding,
encoding: encoding,
options: 0,
range: _SwiftNSRange(location: 0, length: len),
remaining: &remainingRange
Expand All @@ -195,7 +196,23 @@ internal func _cocoaStringCopyUTF8(
_ target: _CocoaString,
into bufPtr: UnsafeMutableRawBufferPointer
) -> Int? {
return _NSStringCopyUTF8(_objc(target), into: bufPtr)
return _NSStringCopyBytes(
_objc(target),
encoding: _cocoaUTF8Encoding,
into: bufPtr
)
}

@_effects(releasenone)
internal func _cocoaStringCopyASCII(
_ target: _CocoaString,
into bufPtr: UnsafeMutableRawBufferPointer
) -> Int? {
return _NSStringCopyBytes(
_objc(target),
encoding: _cocoaASCIIEncoding,
into: bufPtr
)
}

@_effects(readonly)
Expand Down Expand Up @@ -346,17 +363,30 @@ internal func _bridgeTagged(
_internalInvariant(_isObjCTaggedPointer(cocoa))
return _cocoaStringCopyUTF8(cocoa, into: bufPtr)
}

@_effects(releasenone) // @opaque
internal func _bridgeTaggedASCII(
_ cocoa: _CocoaString,
intoUTF8 bufPtr: UnsafeMutableRawBufferPointer
) -> Int? {
_internalInvariant(_isObjCTaggedPointer(cocoa))
return _cocoaStringCopyASCII(cocoa, into: bufPtr)
}
#endif

@_effects(readonly)
private func _NSStringASCIIPointer(_ str: _StringSelectorHolder) -> UnsafePointer<UInt8>? {
// TODO(String bridging): Is there a better interface here? Ideally we'd be
// able to ask for UTF8 rather than just ASCII
//TODO(String bridging): Unconditionally asking for nul-terminated contents is
// overly conservative and hurts perf with some NSStrings
return str._fastCStringContents(1)?._asUInt8
}

@_effects(readonly)
private func _NSStringUTF8Pointer(_ str: _StringSelectorHolder) -> UnsafePointer<UInt8>? {
//We don't have a way to ask for UTF8 here currently
return _NSStringASCIIPointer(str)
}

@_effects(readonly) // @opaque
private func _withCocoaASCIIPointer<R>(
_ str: _CocoaString,
Expand All @@ -371,7 +401,7 @@ private func _withCocoaASCIIPointer<R>(
if requireStableAddress {
return nil // tagged pointer strings don't support _fastCStringContents
}
if let smol = _SmallString(taggedCocoa: str) {
if let smol = _SmallString(taggedASCIICocoa: str) {
return _StringGuts(smol).withFastUTF8 {
work($0.baseAddress._unsafelyUnwrappedUnchecked)
}
Expand All @@ -385,6 +415,34 @@ private func _withCocoaASCIIPointer<R>(
return nil
}

@_effects(readonly) // @opaque
private func _withCocoaUTF8Pointer<R>(
_ str: _CocoaString,
requireStableAddress: Bool,
work: (UnsafePointer<UInt8>) -> R?
) -> R? {
#if !(arch(i386) || arch(arm) || arch(arm64_32))
if _isObjCTaggedPointer(str) {
if let ptr = getConstantTaggedCocoaContents(str)?.asciiContentsPointer {
return work(ptr)
}
if requireStableAddress {
return nil // tagged pointer strings don't support _fastCStringContents
}
if let smol = _SmallString(taggedCocoa: str) {
return _StringGuts(smol).withFastUTF8 {
work($0.baseAddress._unsafelyUnwrappedUnchecked)
}
}
}
#endif
defer { _fixLifetime(str) }
if let ptr = _NSStringUTF8Pointer(_objc(str)) {
return work(ptr)
}
return nil
}

@_effects(readonly) // @opaque
internal func withCocoaASCIIPointer<R>(
_ str: _CocoaString,
Expand All @@ -393,12 +451,26 @@ internal func withCocoaASCIIPointer<R>(
return _withCocoaASCIIPointer(str, requireStableAddress: false, work: work)
}

@_effects(readonly) // @opaque
internal func withCocoaUTF8Pointer<R>(
_ str: _CocoaString,
work: (UnsafePointer<UInt8>) -> R?
) -> R? {
return _withCocoaUTF8Pointer(str, requireStableAddress: false, work: work)
}

@_effects(readonly)
internal func stableCocoaASCIIPointer(_ str: _CocoaString)
-> UnsafePointer<UInt8>? {
return _withCocoaASCIIPointer(str, requireStableAddress: true, work: { $0 })
}

@_effects(readonly)
internal func stableCocoaUTF8Pointer(_ str: _CocoaString)
-> UnsafePointer<UInt8>? {
return _withCocoaUTF8Pointer(str, requireStableAddress: true, work: { $0 })
}

private enum CocoaStringPointer {
case ascii(UnsafePointer<UInt8>)
case utf8(UnsafePointer<UInt8>)
Expand Down
2 changes: 1 addition & 1 deletion stdlib/public/core/StringObject.swift
Original file line number Diff line number Diff line change
Expand Up @@ -915,7 +915,7 @@ extension _StringObject {
_internalInvariant(largeFastIsShared)
#if _runtime(_ObjC)
if largeIsCocoa {
return stableCocoaASCIIPointer(cocoaObject)._unsafelyUnwrappedUnchecked
return stableCocoaUTF8Pointer(cocoaObject)._unsafelyUnwrappedUnchecked
}
#endif

Expand Down
4 changes: 2 additions & 2 deletions stdlib/public/core/StringStorageBridge.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import SwiftShims

#if _runtime(_ObjC)

internal let _cocoaASCIIEncoding:UInt = 1 /* NSASCIIStringEncoding */
internal let _cocoaUTF8Encoding:UInt = 4 /* NSUTF8StringEncoding */
internal var _cocoaASCIIEncoding:UInt { 1 } /* NSASCIIStringEncoding */
internal var _cocoaUTF8Encoding:UInt { 4 } /* NSUTF8StringEncoding */

extension String {
@available(SwiftStdlib 5.6, *)
Expand Down
24 changes: 24 additions & 0 deletions test/stdlib/NSSlowTaggedLocalizedString.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import StdlibUnittest

let longTaggedTests = TestSuite("NonContiguousTaggedStrings")
var constant = "Send Message to different Team"
//doesn't fit in a tagged pointer because of ', but does fit in a SmallString
var shortNonTagged = "Don\u{2019}t Save"

func runEqualLongTagged() {

Expand All @@ -39,5 +41,27 @@ longTaggedTests.test("EqualLongTagged") {
runEqualLongTagged()
}

longTaggedTests.test("EqualNonASCIISubsetSmall") {
if #available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) {
if MemoryLayout<AnyObject>.size != 8 {
return //no tagged pointers
}

var native = shortNonTagged.withUTF8 { String(decoding: $0, as: UTF8.self) }
native.reserveCapacity(30) //force into non-small form so we can reverse bridge below
let longTagged = NSSlowTaggedLocalizedString.createTest()!
shortNonTagged.withCString {
NSSlowTaggedLocalizedString.setContents($0)
}
defer {
NSSlowTaggedLocalizedString.setContents(nil)
}
let reverseBridged = unsafeBitCast(native._guts._object.largeAddressBits, to: AnyObject.self)
let eq = reverseBridged.isEqual(to: longTagged)
expectEqual(eq, 1)
_fixLifetime(native)
}
}

runAllTests()