From 8e039ff590eb01d8eea29bb7d98364932f0416b4 Mon Sep 17 00:00:00 2001 From: Brian Gontowski Date: Tue, 9 Jun 2020 23:10:27 +0900 Subject: [PATCH 1/4] Added CVarArg support to NSString and String --- Sources/Foundation/NSString.swift | 20 ++++++++++++++++++++ Tests/Foundation/Tests/TestNSString.swift | 10 ++++++++++ 2 files changed, 30 insertions(+) diff --git a/Sources/Foundation/NSString.swift b/Sources/Foundation/NSString.swift index 11872e1593..3563e5b965 100644 --- a/Sources/Foundation/NSString.swift +++ b/Sources/Foundation/NSString.swift @@ -1644,3 +1644,23 @@ extension NSString : _StructTypeBridgeable { return _StructType._unconditionallyBridgeFromObjectiveC(self) } } + +extension NSString : CVarArg { + @inlinable // c-abi + public var _cVarArgEncoding: [Int] { + return _encodeBitsAsWords(unsafeBitCast(self, to: CFString.self)) + } +} + +extension String : CVarArg { + @inlinable // c-abi + public var _cVarArgEncoding: [Int] { + // We don't have an autorelease pool to retain the NSString until the withVaList closure is complete. + // So add an operation to release on the next cycle of this thread. + let ns = Unmanaged.passRetained(NSString(string: self)) + OperationQueue.current?.addOperation { + ns.release() + } + return ns.takeUnretainedValue()._cVarArgEncoding + } +} diff --git a/Tests/Foundation/Tests/TestNSString.swift b/Tests/Foundation/Tests/TestNSString.swift index 90ecb00d05..57e56c69a2 100644 --- a/Tests/Foundation/Tests/TestNSString.swift +++ b/Tests/Foundation/Tests/TestNSString.swift @@ -813,6 +813,15 @@ class TestNSString: LoopbackServerTest { } } + func test_initializeWithFormat4() { + let argument: [CVarArg] = ["One", "Two", "Three"] + withVaList(argument) { + pointer in + let string = NSString(format: "Testing %@ %@ %@", arguments: pointer) + XCTAssertEqual(string, "Testing One Two Three") + } + } + func test_appendingPathComponent() { do { let path: NSString = "/tmp" @@ -1677,6 +1686,7 @@ class TestNSString: LoopbackServerTest { ("test_initializeWithFormat", test_initializeWithFormat), ("test_initializeWithFormat2", test_initializeWithFormat2), ("test_initializeWithFormat3", test_initializeWithFormat3), + ("test_initializeWithFormat4", test_initializeWithFormat4), ("test_appendingPathComponent", test_appendingPathComponent), ("test_deletingLastPathComponent", test_deletingLastPathComponent), ("test_getCString_simple", test_getCString_simple), From 0cf72bcd1b8adf4b4e2ae863280abac36e46d3e8 Mon Sep 17 00:00:00 2001 From: Brian Gontowski Date: Thu, 11 Jun 2020 14:47:11 +0900 Subject: [PATCH 2/4] Implemented _CVarArgObject protocol to retain the NSString --- Sources/Foundation/NSString.swift | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Sources/Foundation/NSString.swift b/Sources/Foundation/NSString.swift index 3563e5b965..8290e4844d 100644 --- a/Sources/Foundation/NSString.swift +++ b/Sources/Foundation/NSString.swift @@ -1652,15 +1652,14 @@ extension NSString : CVarArg { } } -extension String : CVarArg { +extension String : CVarArg, _CVarArgObject { + @inlinable // c-abi + public var _cVarArgObject: CVarArg { + return NSString(string: self) + } + @inlinable // c-abi public var _cVarArgEncoding: [Int] { - // We don't have an autorelease pool to retain the NSString until the withVaList closure is complete. - // So add an operation to release on the next cycle of this thread. - let ns = Unmanaged.passRetained(NSString(string: self)) - OperationQueue.current?.addOperation { - ns.release() - } - return ns.takeUnretainedValue()._cVarArgEncoding + fatalError("_cVarArgEncoding must be called on NSString instead") } } From 2260aefce4f89e59b0dd387fa694f00811965282 Mon Sep 17 00:00:00 2001 From: Brian Gontowski Date: Sat, 12 Sep 2020 22:32:27 +0900 Subject: [PATCH 3/4] _CVarArgObject is currently only available on platforms without ObjC runtime --- Sources/Foundation/NSString.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sources/Foundation/NSString.swift b/Sources/Foundation/NSString.swift index 8290e4844d..bae4973217 100644 --- a/Sources/Foundation/NSString.swift +++ b/Sources/Foundation/NSString.swift @@ -1652,6 +1652,7 @@ extension NSString : CVarArg { } } +#if !_runtime(_ObjC) extension String : CVarArg, _CVarArgObject { @inlinable // c-abi public var _cVarArgObject: CVarArg { @@ -1663,3 +1664,4 @@ extension String : CVarArg, _CVarArgObject { fatalError("_cVarArgEncoding must be called on NSString instead") } } +#endif From b7c4b8cc3373a627872f0da78e1cca2a22ce9f86 Mon Sep 17 00:00:00 2001 From: Brian Gontowski Date: Fri, 2 Oct 2020 14:07:54 +0900 Subject: [PATCH 4/4] Return a pointer using AnyObject instead of CFString --- Sources/Foundation/NSString.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Foundation/NSString.swift b/Sources/Foundation/NSString.swift index bae4973217..dbe94c8b17 100644 --- a/Sources/Foundation/NSString.swift +++ b/Sources/Foundation/NSString.swift @@ -1648,7 +1648,7 @@ extension NSString : _StructTypeBridgeable { extension NSString : CVarArg { @inlinable // c-abi public var _cVarArgEncoding: [Int] { - return _encodeBitsAsWords(unsafeBitCast(self, to: CFString.self)) + return _encodeBitsAsWords(unsafeBitCast(self, to: AnyObject.self)) } }