Skip to content

Commit b21d8be

Browse files
author
Dave Abrahams
committed
[stdlib] Bridging [aClass] to ObjC in O(1) (redux #2)
There's no need for a deferred conversion in these cases. This time committing ALL the changes needed to get the validation tests to pass.
1 parent 4d2a477 commit b21d8be

File tree

7 files changed

+96
-108
lines changed

7 files changed

+96
-108
lines changed

stdlib/public/core/Arrays.swift.gyb

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2082,13 +2082,12 @@ extension Array {
20822082
public static func _bridgeFromObjectiveCAdoptingNativeStorageOf(
20832083
_ source: AnyObject
20842084
) -> Array? {
2085-
if let deferred = source as? _SwiftDeferredNSArray {
2086-
if let nativeStorage =
2087-
deferred._nativeStorage as? _ContiguousArrayStorage<Element> {
2088-
return Array(_ContiguousArrayBuffer(nativeStorage))
2089-
}
2085+
// If source is deferred, we indirect to get its native storage
2086+
let maybeNative = (source as? _SwiftDeferredNSArray)?._nativeStorage ?? source
2087+
2088+
return (maybeNative as? _ContiguousArrayStorage<Element>).map {
2089+
Array(_ContiguousArrayBuffer($0))
20902090
}
2091-
return nil
20922091
}
20932092

20942093
/// Private initializer used for bridging.

stdlib/public/core/ContiguousArrayBuffer.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -433,8 +433,10 @@ struct _ContiguousArrayBuffer<Element> : _ArrayBufferProtocol {
433433
_isBridgedToObjectiveC(Element.self),
434434
"Array element type is not bridged to Objective-C")
435435
if count == 0 {
436-
return _SwiftDeferredNSArray(
437-
_nativeStorage: _emptyArrayStorage)
436+
return _emptyArrayStorage
437+
}
438+
if _isBridgedVerbatimToObjectiveC(Element.self) {
439+
return _storage
438440
}
439441
return _SwiftDeferredNSArray(_nativeStorage: _storage)
440442
}

stdlib/public/core/Runtime.swift.gyb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ public final class _stdlib_AtomicInt {
290290
}
291291

292292
% for operation_name, operation in [ ('Add', '+'), ('And', '&'), ('Or', '|'), ('Xor', '^') ]:
293+
@discardableResult
293294
public func fetchAnd${operation_name}(_ operand: Int) -> Int {
294295
return _swift_stdlib_atomicFetch${operation_name}Int(
295296
object: _valuePtr,

test/1_stdlib/ArrayBridge.swift

Lines changed: 63 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,8 @@
2323

2424
import Foundation
2525
import ArrayBridgeObjC
26-
27-
// CHECK: testing...
28-
print("testing...")
26+
import StdlibUnittest
27+
let tests = TestSuite("ArrayBridge")
2928

3029
var trackedCount = 0
3130
var nextTrackedSerialNumber = 0
@@ -67,14 +66,14 @@ class Tracked : NSObject, Fooable {
6766
return self.dynamicType.init(self.value + 1)
6867
}
6968

69+
override func isEqual(_ other: AnyObject?) -> Bool {
70+
return (other as? Tracked)?.value == self.value
71+
}
72+
7073
var value: Int
7174
var serialNumber: Int
7275
}
7376

74-
func == (x: Tracked, y: Tracked) -> Bool {
75-
return x.value == y.value
76-
}
77-
7877
typealias Base = Tracked
7978
class Derived : Base, Barable {
8079
func bar() { }
@@ -201,14 +200,16 @@ class Thunks : NSObject {
201200
// Base is "bridged verbatim"
202201
//===----------------------------------------------------------------------===//
203202

204-
func testBridgedVerbatim() {
203+
tests.test("testBridgedVerbatim") {
204+
nextTrackedSerialNumber = 0
205205
let bases: [Base] = [Base(100), Base(200), Base(300)]
206206

207207
//===--- Implicit conversion to/from NSArray ------------------------------===//
208208

209-
// CHECK-NEXT: Base#1(100)
210209
let basesConvertedToNSArray = bases as NSArray
211-
print(basesConvertedToNSArray.object(at: 0) as! Base)
210+
expectEqual(
211+
"Base#1(100)",
212+
String(basesConvertedToNSArray.object(at: 0) as! Base))
212213

213214
// Create an ordinary NSArray, not a native one
214215
let nsArrayOfBase: NSArray = NSArray(object: Base(42))
@@ -217,107 +218,79 @@ func testBridgedVerbatim() {
217218
let nsArrayOfBaseConvertedToAnyObjectArray = nsArrayOfBase as [AnyObject]
218219

219220
// Capture the representation of the first element
220-
// CHECK-NEXT: [[base42:Base.*42]]
221-
print(nsArrayOfBase.object(at: 0) as! Base)
221+
let base42: ObjectIdentifier
222+
do {
223+
let b = nsArrayOfBase.object(at: 0) as! Base
224+
expectEqual(42, b.value)
225+
base42 = ObjectIdentifier(b)
226+
}
222227

223228
// ...with the same elements
224-
// CHECK-NEXT: [[base42]]
225-
print(nsArrayOfBaseConvertedToAnyObjectArray[0] as! Base)
229+
expectEqual(
230+
base42,
231+
ObjectIdentifier(nsArrayOfBaseConvertedToAnyObjectArray[0] as! Base))
226232

227233
// Verify that NSArray class methods are inherited by a Swift bridging class.
228-
// CHECK-NEXT: Swift.{{.*}}Array
229-
debugPrint(basesConvertedToNSArray.dynamicType)
230-
// CHECK-NEXT: true
231-
print(basesConvertedToNSArray.dynamicType.supportsSecureCoding)
234+
let className = String(reflecting: basesConvertedToNSArray.dynamicType)
235+
expectTrue(className.hasPrefix("Swift._ContiguousArrayStorage"))
236+
expectTrue(basesConvertedToNSArray.dynamicType.supportsSecureCoding)
232237

233238
//===--- Up- and Down-casts -----------------------------------------------===//
234239
var derived: [Derived] = [Derived(11), Derived(22)]
235-
// CHECK-NEXT: [[derived0:\[Derived#[0-9]+\(11\), Derived#[0-9]+\(22\)\]{1}]]
236-
print(derived)
240+
let derived0 = derived
237241

238242
// upcast is implicit
239243
let derivedAsBases: [Base] = derived
240-
241-
// CHECK-NEXT: [[derived0]]
242-
print(derivedAsBases)
244+
expectEqual(derived.count, derivedAsBases.count)
245+
for (x, y) in zip(derived, derivedAsBases) {
246+
expectTrue(x === y)
247+
}
243248

244249
// Arrays are logically distinct after upcast
245250
derived[0] = Derived(33)
246-
247-
// CHECK-NEXT: {{\[Derived#[0-9]+\(33\), Derived#[0-9]+\(22\)]}}
248-
print(derived)
249-
// CHECK-NEXT: [[derived0]]
250-
print(derivedAsBases)
251251

252-
// CHECK-NEXT: [[derived0]]
253-
if let roundTripDerived = derivedAsBases as? [Derived] {
254-
print(roundTripDerived)
255-
}
256-
else {
257-
print("roundTripDerived upcast failed")
258-
}
252+
expectEqual([Derived(33), Derived(22)], derived)
253+
expectEqual([Derived(11), Derived(22)], derivedAsBases)
254+
255+
expectEqual(derived0, derivedAsBases as! [Derived])
259256

260-
// CHECK-NEXT: [[derived2:\[Derived#[0-9]+\(44\), Derived#[0-9]+\(55\)\]{1}]]
261257
let derivedInBaseBuffer: [Base] = [Derived(44), Derived(55)]
262-
print(derivedInBaseBuffer)
258+
let derived2 = derivedInBaseBuffer
263259

264-
// CHECK-NEXT: Explicit downcast-ability is based on element type, not buffer type
265-
if let downcastBaseBuffer = derivedInBaseBuffer as? [Derived] {
266-
print("Explicit downcast-ability is based on element type, not buffer type")
267-
}
268-
else {
269-
print("Unexpected downcast failure")
270-
}
260+
// Explicit downcast-ability is based on element type, not buffer type
261+
expectNotEmpty(derivedInBaseBuffer as? [Derived])
271262

272263
// We can up-cast to array of AnyObject
273-
// CHECK-NEXT: [[derived2]]
274264
let derivedAsAnyObjectArray: [AnyObject] = derivedInBaseBuffer
275-
print(derivedAsAnyObjectArray)
265+
expectEqual(derived2, derivedAsAnyObjectArray.map { $0 as! Base })
276266

277-
// CHECK-NEXT: downcastBackToBase = [[derived2]]
278-
if let downcastBackToBase = derivedAsAnyObjectArray as? [Base] {
279-
print("downcastBackToBase = \(downcastBackToBase)")
280-
}
281-
else {
282-
print("downcastBackToBase failed")
283-
}
267+
let downcastBackToBase = derivedAsAnyObjectArray as? [Base]
268+
expectNotEmpty(downcastBackToBase)
284269

285-
// CHECK-NEXT: downcastBackToDerived = [[derived2]]
286-
if let downcastBackToDerived = derivedAsAnyObjectArray as? [Derived] {
287-
print("downcastBackToDerived = \(downcastBackToDerived)")
288-
}
289-
else {
290-
print("downcastBackToDerived failed")
270+
if let downcastBackToDerived = expectNotEmpty(derivedAsAnyObjectArray as? [Derived]) {
271+
expectEqual(derived2, downcastBackToDerived)
291272
}
292273

293-
// CHECK-NEXT: downcastToProtocols = [[derived2]]
294-
if let downcastToProtocols = derivedAsAnyObjectArray as? [Fooable] {
295-
print("downcastToProtocols = \(downcastToProtocols)")
296-
} else {
297-
print("downcastToProtocols failed")
274+
if let downcastToProtocols = expectNotEmpty(derivedAsAnyObjectArray as? [Fooable]) {
275+
expectEqual(derived2, downcastToProtocols.map { $0 as! Derived })
298276
}
299277

300-
// CHECK-NEXT: downcastToProtocols = [[derived2]]
301-
if let downcastToProtocols = derivedAsAnyObjectArray as? [Barable] {
302-
print("downcastToProtocols = \(downcastToProtocols)")
303-
} else {
304-
print("downcastToProtocols failed")
278+
if let downcastToProtocols = expectNotEmpty(derivedAsAnyObjectArray as? [Barable]) {
279+
expectEqual(derived2, downcastToProtocols.map { $0 as! Derived })
305280
}
306281

307-
// CHECK-NEXT: downcastToProtocols = [[derived2]]
308-
if let downcastToProtocols = derivedAsAnyObjectArray as? [protocol<Barable, Fooable>] {
309-
print("downcastToProtocols = \(downcastToProtocols)")
310-
} else {
311-
print("downcastToProtocols failed")
282+
if let downcastToProtocols = expectNotEmpty(derivedAsAnyObjectArray as? [protocol<Barable, Fooable>]) {
283+
expectEqual(derived2, downcastToProtocols.map { $0 as! Derived })
312284
}
313285

314-
// CHECK-NEXT: downcastToProtocols failed
315-
if let downcastToProtocols = derivedAsAnyObjectArray as? [protocol<Barable, Bazable>] {
316-
print("downcastToProtocols = \(downcastToProtocols)")
317-
} else {
318-
print("downcastToProtocols failed")
319-
}
286+
expectEmpty(derivedAsAnyObjectArray as? [protocol<Barable, Bazable>])
287+
}
320288

289+
func doTestBridgedObjC() {
290+
// CHECK: doTestBridgedObjC
291+
print("doTestBridgedObjC")
292+
293+
testBridgedObjC(Thunks())
321294
// CHECK-NEXT: produceBridgedObjCArray([BridgedObjC[[A:#[0-9]+]](0), BridgedObjC[[B:#[0-9]+]](1), BridgedObjC[[C:#[0-9]+]](2), BridgedObjC[[D:#[0-9]+]](3), BridgedObjC[[E:#[0-9]+]](4)])
322295
testBridgedObjC(Thunks())
323296
// CHECK-NEXT: 5 elements in the array
@@ -329,7 +302,7 @@ func testBridgedVerbatim() {
329302

330303
// CHECK-NEXT: acceptBridgedObjCArray([BridgedObjC[[A:#[0-9]+]](10), BridgedObjC[[B:#[0-9]+]](11), BridgedObjC[[C:#[0-9]+]](12), BridgedObjC[[D:#[0-9]+]](13), BridgedObjC[[E:#[0-9]+]](14)])
331304
}
332-
testBridgedVerbatim()
305+
doTestBridgedObjC()
333306

334307
//===--- Explicitly Bridged -----------------------------------------------===//
335308
// BridgedSwift conforms to _ObjectiveCBridgeable
@@ -454,7 +427,7 @@ func testExplicitlyBridged() {
454427

455428
// Downcast of Cocoa array to an array of strings (which should fail)
456429
// CHECK-NEXT: Could not downcast [AnyObject] to [String]
457-
if let downcasted = wrappedCocoaBridgedSwifts as? [String] {
430+
if let _ = wrappedCocoaBridgedSwifts as? [String] {
458431
print("Shouldn't be able to downcast to an array of strings")
459432
} else {
460433
print("Could not downcast [AnyObject] to [String]")
@@ -473,7 +446,7 @@ func testExplicitlyBridged() {
473446

474447
// Downcast from a nil implicitly unwrapped optional array of AnyObjects.
475448
wrappedCocoaBridgedSwiftsIUO = nil
476-
if let downcasted = wrappedCocoaBridgedSwiftsIUO as? [BridgedSwift] {
449+
if let _ = wrappedCocoaBridgedSwiftsIUO as? [BridgedSwift] {
477450
print("Cannot downcast from a nil array!")
478451
} else {
479452
// CHECK-NEXT: Correctly rejected downcast of nil array
@@ -493,7 +466,7 @@ func testExplicitlyBridged() {
493466

494467
// Downcast from a nil optional array of AnyObjects.
495468
wrappedCocoaBridgedSwiftsOpt = nil
496-
if let downcasted = wrappedCocoaBridgedSwiftsOpt as? [BridgedSwift] {
469+
if let _ = wrappedCocoaBridgedSwiftsOpt as? [BridgedSwift] {
497470
print("Cannot downcast from a nil array!")
498471
} else {
499472
// CHECK-NEXT: Correctly rejected downcast of nil array
@@ -517,12 +490,9 @@ func testRoundTrip() {
517490
class Test : NSObject {
518491
@objc dynamic func call(_ array: NSArray) -> NSArray {
519492

520-
// CHECK-NEXT: ---Passed array---
521-
print("---Passed array---")
522493
let result = array as! [BridgedSwift]
523-
// CHECK-NEXT: bridge operations (from, to) = (0, 0)
524-
BridgedSwift.printStats()
525-
494+
expectEqual(0, bridgeFromOperationCount)
495+
expectEqual(0, bridgeToOperationCount)
526496

527497
// Clear out the stats before returning array
528498
BridgedSwift.resetStats()
@@ -537,12 +507,10 @@ func testRoundTrip() {
537507
BridgedSwift(40), BridgedSwift(50) ]
538508

539509
BridgedSwift.resetStats()
540-
test.call(array as NSArray)
510+
_ = test.call(array as NSArray)
541511

542-
// CHECK-NEXT: ---Returned Array---
543-
print("---Returned Array---")
544-
// CHECK-NEXT: bridge operations (from, to) = (0, 0)
545-
BridgedSwift.printStats()
512+
expectEqual(0, bridgeFromOperationCount)
513+
expectEqual(0, bridgeToOperationCount)
546514
}
547515
testRoundTrip()
548516
//===--- Non-bridging -----------------------------------------------------===//
@@ -566,5 +534,4 @@ func testMutableArray() {
566534
}
567535
testMutableArray()
568536

569-
// CHECK-NEXT: done.
570-
print("done.")
537+
runAllTests()

test/1_stdlib/Inputs/DictionaryKeyValueTypesObjC.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@ func isCocoaNSDictionary(_ d: NSDictionary) -> Bool {
4646

4747
func isNativeNSArray(_ d: NSArray) -> Bool {
4848
let className: NSString = NSStringFromClass(d.dynamicType) as NSString
49-
return className.range(of: "_SwiftDeferredNSArray").length > 0
49+
return ["_SwiftDeferredNSArray", "_ContiguousArray", "_EmptyArray"].contains {
50+
className.range(of: $0).length > 0
51+
}
5052
}
5153

5254
var _objcKeyCount = _stdlib_AtomicInt(0)

test/Interpreter/SDK/objc_fast_enumeration.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,19 @@ var nsa2 = (a2._buffer._asCocoaArray() as AnyObject) as! NSArray
9595
for x: AnyObject in nsa2 {
9696
print(x.description!)
9797
}
98+
99+
class X : CustomStringConvertible {
100+
init(_ value: Int) { self.value = value }
101+
var value: Int
102+
var description: String { return "X(\(value))" }
103+
}
104+
105+
// Enumeration over a _ContiguousArrayBuffer
106+
// CHECK: X(3)
107+
// CHECK: X(2)
108+
// CHECK: X(1)
109+
var a3 = [X(3), X(2), X(1)]
110+
var nsa3 = (a3._buffer._asCocoaArray() as AnyObject) as! NSArray
111+
for x: AnyObject in nsa3 {
112+
print(x.description!)
113+
}

validation-test/stdlib/ArrayNew.swift.gyb

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ ArrayTestSuite.test("BridgedFromObjC.Verbatim.ArrayIsCopied") {
386386
ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.ArrayIsCopied") {
387387
let source = [ 10, 20, 30 ]
388388
let nsa = getAsNSMutableArray(source)
389-
var result = nsa as! Array<TestBridgedValueTy>
389+
var result = nsa as AnyObject as! Array<TestBridgedValueTy>
390390
expectTrue(isNativeArray(result))
391391

392392
// Delete the value from NSMutableArray.
@@ -740,8 +740,9 @@ ArrayTestSuite.test("BridgedToObjC/Verbatim/BridgeBack/Reallocate") {
740740

741741
ArrayTestSuite.test("BridgedToObjC/Verbatim/BridgeBack/Adopt") {
742742
// Bridge back to native array.
743-
var native: [TestObjCValueTy] = convertNSArrayToArray(
744-
getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 3))
743+
var native: [TestObjCValueTy] =
744+
getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 3) as! Array
745+
745746
let identity1 = unsafeBitCast(native, to: UInt.self)
746747

747748
// Mutate elements, but don't change count.

0 commit comments

Comments
 (0)