Skip to content

Sync NSStringAPI.swift with swift-corelibs-foundation #11474

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
Aug 22, 2017
Merged
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
86 changes: 70 additions & 16 deletions stdlib/public/SDK/Foundation/NSStringAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,19 @@
//
//===----------------------------------------------------------------------===//

// Important Note
// ==============
//
// This file is shared between two projects:
//
// 1. https://github.com/apple/swift/tree/master/stdlib/public/SDK/Foundation
// 2. https://github.com/apple/swift-corelibs-foundation/tree/master/Foundation
//
// If you change this file, you must update it in both places.

#if !DEPLOYMENT_RUNTIME_SWIFT
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please document this variable in the comment as well?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the same define we use in Data and other Foundation overlay code that is shared. It is the demarcation that the underlying runtime is not objc; ala swift-corelibs-foundation.

It is worth noting that it is NOT the same as the #if _runtime(_ObjC) define since that is true when we build swift-corelibs-foundation on Mac; and we need a conditional to be a "this is being built for swift-corelibs-foundation"

@_exported import Foundation // Clang module
#endif

// Open Issues
// ===========
Expand All @@ -36,6 +48,7 @@ func _toNSRange(_ r: Range<String.Index>) -> NSRange {
length: r.upperBound.encodedOffset - r.lowerBound.encodedOffset)
}

#if !DEPLOYMENT_RUNTIME_SWIFT
// We only need this for UnsafeMutablePointer, but there's not currently a way
// to write that constraint.
extension Optional {
Expand All @@ -57,6 +70,7 @@ extension Optional {
return self == nil ? body(nil) : body(&object)
}
}
#endif

extension String {
//===--- Class Methods --------------------------------------------------===//
Expand Down Expand Up @@ -156,7 +170,7 @@ extension String {
/// C array of UTF8-encoded bytes.
public init?(utf8String bytes: UnsafePointer<CChar>) {
if let ns = NSString(utf8String: bytes) {
self = ns as String
self = String._unconditionallyBridgeFromObjectiveC(ns)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any benefit to explicitly bridge here and elsewhere as opposed to implicitly bridge via as, other than... well, explicit-ness?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as does not work on linux; but we do have _unconditionallyBridgeFromObjectiveC because we need to have the same functionality.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implicit bridging is not supported on Linux, so by explicitly bridging we can keep this code common and avoid more #ifs.

Copy link
Contributor

@jckarter jckarter Aug 15, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_unconditionallyBridgeFromObjectiveC really seems like something that should not exist on Linux. Is there a reason you can't just use String(NSString)?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we have to have a common protocol interface so that we can deal with the Any case. That basically boiled down to the concept that the _ObjectiveCBridgeable is public access level and was exposed API on these types. Converting to the initializers will be a penalty to the Darwin versions (which is a non-starter in my book).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to continue the conversation about Linux bridging, but separately from this commit.

} else {
return nil
}
Expand Down Expand Up @@ -185,7 +199,7 @@ extension String {
if let ns = NSString(
bytes: byteArray, length: byteArray.count, encoding: encoding.rawValue) {

self = ns as String
self = String._unconditionallyBridgeFromObjectiveC(ns)
} else {
return nil
}
Expand All @@ -210,7 +224,7 @@ extension String {
bytesNoCopy: bytes, length: length, encoding: encoding.rawValue,
freeWhenDone: flag) {

self = ns as String
self = String._unconditionallyBridgeFromObjectiveC(ns)
} else {
return nil
}
Expand All @@ -227,7 +241,7 @@ extension String {
utf16CodeUnits: UnsafePointer<unichar>,
count: Int
) {
self = NSString(characters: utf16CodeUnits, length: count) as String
self = String._unconditionallyBridgeFromObjectiveC(NSString(characters: utf16CodeUnits, length: count))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please keep it within 80 columns.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is the swift standard library formatting guidelines; Foundation is not limited as such (this file probably should be reformatted to match the rest of our style guides)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed with @phausler on this point, since we are the maintainers of this code.

}

// - (instancetype)
Expand All @@ -242,10 +256,10 @@ extension String {
count: Int,
freeWhenDone flag: Bool
) {
self = NSString(
self = String._unconditionallyBridgeFromObjectiveC(NSString(
charactersNoCopy: UnsafeMutablePointer(mutating: utf16CodeUnitsNoCopy),
length: count,
freeWhenDone: flag) as String
freeWhenDone: flag))
}

//===--- Initializers that can fail -------------------------------------===//
Expand All @@ -263,7 +277,7 @@ extension String {
encoding enc: Encoding
) throws {
let ns = try NSString(contentsOfFile: path, encoding: enc.rawValue)
self = ns as String
self = String._unconditionallyBridgeFromObjectiveC(ns)
}

// - (instancetype)
Expand All @@ -281,14 +295,14 @@ extension String {
var enc: UInt = 0
let ns = try NSString(contentsOfFile: path, usedEncoding: &enc)
usedEncoding = Encoding(rawValue: enc)
self = ns as String
self = String._unconditionallyBridgeFromObjectiveC(ns)
}

public init(
contentsOfFile path: String
) throws {
let ns = try NSString(contentsOfFile: path, usedEncoding: nil)
self = ns as String
self = String._unconditionallyBridgeFromObjectiveC(ns)
}

// - (instancetype)
Expand All @@ -304,7 +318,7 @@ extension String {
encoding enc: Encoding
) throws {
let ns = try NSString(contentsOf: url, encoding: enc.rawValue)
self = ns as String
self = String._unconditionallyBridgeFromObjectiveC(ns)
}

// - (instancetype)
Expand All @@ -322,14 +336,14 @@ extension String {
var enc: UInt = 0
let ns = try NSString(contentsOf: url as URL, usedEncoding: &enc)
usedEncoding = Encoding(rawValue: enc)
self = ns as String
self = String._unconditionallyBridgeFromObjectiveC(ns)
}

public init(
contentsOf url: URL
) throws {
let ns = try NSString(contentsOf: url, usedEncoding: nil)
self = ns as String
self = String._unconditionallyBridgeFromObjectiveC(ns)
}

// - (instancetype)
Expand All @@ -343,7 +357,7 @@ extension String {
encoding enc: Encoding
) {
if let ns = NSString(cString: cString, encoding: enc.rawValue) {
self = ns as String
self = String._unconditionallyBridgeFromObjectiveC(ns)
} else {
return nil
}
Expand All @@ -359,7 +373,7 @@ extension String {
/// Unicode characters using a given `encoding`.
public init?(data: Data, encoding: Encoding) {
guard let s = NSString(data: data, encoding: encoding.rawValue) else { return nil }
self = s as String
self = String._unconditionallyBridgeFromObjectiveC(s)
}

// - (instancetype)initWithFormat:(NSString *)format, ...
Expand Down Expand Up @@ -400,9 +414,17 @@ extension String {
/// format string as a template into which the remaining argument
/// values are substituted according to given locale information.
public init(format: String, locale: Locale?, arguments: [CVarArg]) {
#if DEPLOYMENT_RUNTIME_SWIFT
self = withVaList(arguments) {
String._unconditionallyBridgeFromObjectiveC(
NSString(format: format, locale: locale?._bridgeToObjectiveC(), arguments: $0)
)
}
#else
self = withVaList(arguments) {
NSString(format: format, locale: locale, arguments: $0) as String
}
#endif
}

}
Expand All @@ -414,7 +436,7 @@ extension StringProtocol where Index == String.Index {
/// The corresponding `NSString` - a convenience for bridging code.
// FIXME(strings): There is probably a better way to bridge Self to NSString
var _ns: NSString {
return self._ephemeralString as NSString
return self._ephemeralString._bridgeToObjectiveC()
}

/// Return an `Index` corresponding to the given offset in our UTF-16
Expand Down Expand Up @@ -584,7 +606,7 @@ extension StringProtocol where Index == String.Index {
range: _toNSRange(
range ?? startIndex..<endIndex
),
locale: locale
locale: locale?._bridgeToObjectiveC()
)

: range != nil ? _ns.compare(
Expand Down Expand Up @@ -616,6 +638,23 @@ extension StringProtocol where Index == String.Index {
matchesInto outputArray: UnsafeMutablePointer<[String]>? = nil,
filterTypes: [String]? = nil
) -> Int {
#if DEPLOYMENT_RUNTIME_SWIFT
var outputNamePlaceholder: String?
var outputArrayPlaceholder = [String]()
let res = self._ns.completePath(
into: &outputNamePlaceholder,
caseSensitive: caseSensitive,
matchesInto: &outputArrayPlaceholder,
filterTypes: filterTypes
)
if let n = outputNamePlaceholder {
outputName?.pointee = n
} else {
outputName?.pointee = ""
}
outputArray?.pointee = outputArrayPlaceholder
return res
#else // DEPLOYMENT_RUNTIME_SWIFT
var nsMatches: NSArray?
var nsOutputName: NSString?

Expand Down Expand Up @@ -647,6 +686,7 @@ extension StringProtocol where Index == String.Index {
outputName?.pointee = n as String
}
return result
#endif // DEPLOYMENT_RUNTIME_SWIFT
}

// - (NSArray *)
Expand Down Expand Up @@ -875,6 +915,7 @@ extension StringProtocol where Index == String.Index {
return _ns.precomposedStringWithCompatibilityMapping
}

#if !DEPLOYMENT_RUNTIME_SWIFT
// - (id)propertyList

/// Parses the `String` as a text representation of a
Expand All @@ -891,6 +932,7 @@ extension StringProtocol where Index == String.Index {
public func propertyListFromStringsFileFormat() -> [String : String] {
return _ns.propertyListFromStringsFileFormat() as! [String : String]? ?? [:]
}
#endif

// - (BOOL)localizedStandardContainsString:(NSString *)str NS_AVAILABLE(10_11, 9_0);

Expand Down Expand Up @@ -1048,6 +1090,7 @@ extension StringProtocol where Index == String.Index {
: _ns.replacingOccurrences(of: target, with: replacement)
}

#if !DEPLOYMENT_RUNTIME_SWIFT
// - (NSString *)
// stringByReplacingPercentEscapesUsingEncoding:(NSStringEncoding)encoding

Expand All @@ -1061,6 +1104,7 @@ extension StringProtocol where Index == String.Index {
) -> String? {
return _ns.replacingPercentEscapes(using: encoding.rawValue)
}
#endif

// - (NSString *)stringByTrimmingCharactersInSet:(NSCharacterSet *)set

Expand Down Expand Up @@ -1129,6 +1173,7 @@ extension StringProtocol where Index == String.Index {

// - (nullable NSString *)stringByApplyingTransform:(NSString *)transform reverse:(BOOL)reverse NS_AVAILABLE(10_11, 9_0);

#if !DEPLOYMENT_RUNTIME_SWIFT
/// Perform string transliteration.
@available(OSX 10.11, iOS 9.0, *)
public func applyingTransform(
Expand Down Expand Up @@ -1175,6 +1220,7 @@ extension StringProtocol where Index == String.Index {
}
}
}
#endif

// - (void)
// enumerateSubstringsInRange:(NSRange)range
Expand Down Expand Up @@ -1385,6 +1431,7 @@ extension StringProtocol where Index == String.Index {
return _range(_ns.lineRange(for: _toNSRange(aRange.relative(to: self))))
}

#if !DEPLOYMENT_RUNTIME_SWIFT
// - (NSArray *)
// linguisticTagsInRange:(NSRange)range
// scheme:(NSString *)tagScheme
Expand Down Expand Up @@ -1432,6 +1479,7 @@ extension StringProtocol where Index == String.Index {
return _range(
_ns.paragraphRange(for: _toNSRange(aRange.relative(to: self))))
}
#endif

// - (NSRange)rangeOfCharacterFromSet:(NSCharacterSet *)aSet
//
Expand Down Expand Up @@ -1553,6 +1601,7 @@ extension StringProtocol where Index == String.Index {
_ns.localizedStandardRange(of: string._ephemeralString))
}

#if !DEPLOYMENT_RUNTIME_SWIFT
// - (NSString *)
// stringByAddingPercentEscapesUsingEncoding:(NSStringEncoding)encoding

Expand All @@ -1566,6 +1615,7 @@ extension StringProtocol where Index == String.Index {
) -> String? {
return _ns.addingPercentEscapes(using: encoding.rawValue)
}
#endif

//===--- From the 10.10 release notes; not in public documentation ------===//
// No need to make these unavailable on earlier OSes, since they can
Expand Down Expand Up @@ -1874,6 +1924,7 @@ extension StringProtocol {
fatalError("unavailable function can't be called")
}

#if !DEPLOYMENT_RUNTIME_SWIFT
@available(*, unavailable, renamed: "enumerateLinguisticTags(in:scheme:options:orthography:_:)")
public func enumerateLinguisticTagsIn(
_ range: Range<Index>,
Expand All @@ -1885,6 +1936,7 @@ extension StringProtocol {
) {
fatalError("unavailable function can't be called")
}
#endif

@available(*, unavailable, renamed: "enumerateSubstrings(in:options:_:)")
public func enumerateSubstringsIn(
Expand Down Expand Up @@ -1941,6 +1993,7 @@ extension StringProtocol {
fatalError("unavailable function can't be called")
}

#if !DEPLOYMENT_RUNTIME_SWIFT
@available(*, unavailable, renamed: "linguisticTags(in:scheme:options:orthography:tokenRanges:)")
public func linguisticTagsIn(
_ range: Range<Index>,
Expand All @@ -1951,6 +2004,7 @@ extension StringProtocol {
) -> [String] {
fatalError("unavailable function can't be called")
}
#endif

@available(*, unavailable, renamed: "lowercased(with:)")
public func lowercaseStringWith(_ locale: Locale?) -> String {
Expand Down