Skip to content

Commit

Permalink
[stdlib] Bridging [aClass] to ObjC in O(1) (redux #2)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Dave Abrahams committed Jul 14, 2016
1 parent 4d2a477 commit b21d8be
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 108 deletions.
11 changes: 5 additions & 6 deletions stdlib/public/core/Arrays.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -2082,13 +2082,12 @@ extension Array {
public static func _bridgeFromObjectiveCAdoptingNativeStorageOf(
_ source: AnyObject
) -> Array? {
if let deferred = source as? _SwiftDeferredNSArray {
if let nativeStorage =
deferred._nativeStorage as? _ContiguousArrayStorage<Element> {
return Array(_ContiguousArrayBuffer(nativeStorage))
}
// If source is deferred, we indirect to get its native storage
let maybeNative = (source as? _SwiftDeferredNSArray)?._nativeStorage ?? source

return (maybeNative as? _ContiguousArrayStorage<Element>).map {
Array(_ContiguousArrayBuffer($0))
}
return nil
}

/// Private initializer used for bridging.
Expand Down
6 changes: 4 additions & 2 deletions stdlib/public/core/ContiguousArrayBuffer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -433,8 +433,10 @@ struct _ContiguousArrayBuffer<Element> : _ArrayBufferProtocol {
_isBridgedToObjectiveC(Element.self),
"Array element type is not bridged to Objective-C")
if count == 0 {
return _SwiftDeferredNSArray(
_nativeStorage: _emptyArrayStorage)
return _emptyArrayStorage
}
if _isBridgedVerbatimToObjectiveC(Element.self) {
return _storage
}
return _SwiftDeferredNSArray(_nativeStorage: _storage)
}
Expand Down
1 change: 1 addition & 0 deletions stdlib/public/core/Runtime.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ public final class _stdlib_AtomicInt {
}

% for operation_name, operation in [ ('Add', '+'), ('And', '&'), ('Or', '|'), ('Xor', '^') ]:
@discardableResult
public func fetchAnd${operation_name}(_ operand: Int) -> Int {
return _swift_stdlib_atomicFetch${operation_name}Int(
object: _valuePtr,
Expand Down
159 changes: 63 additions & 96 deletions test/1_stdlib/ArrayBridge.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,8 @@

import Foundation
import ArrayBridgeObjC

// CHECK: testing...
print("testing...")
import StdlibUnittest
let tests = TestSuite("ArrayBridge")

var trackedCount = 0
var nextTrackedSerialNumber = 0
Expand Down Expand Up @@ -67,14 +66,14 @@ class Tracked : NSObject, Fooable {
return self.dynamicType.init(self.value + 1)
}

override func isEqual(_ other: AnyObject?) -> Bool {
return (other as? Tracked)?.value == self.value
}

var value: Int
var serialNumber: Int
}

func == (x: Tracked, y: Tracked) -> Bool {
return x.value == y.value
}

typealias Base = Tracked
class Derived : Base, Barable {
func bar() { }
Expand Down Expand Up @@ -201,14 +200,16 @@ class Thunks : NSObject {
// Base is "bridged verbatim"
//===----------------------------------------------------------------------===//

func testBridgedVerbatim() {
tests.test("testBridgedVerbatim") {
nextTrackedSerialNumber = 0
let bases: [Base] = [Base(100), Base(200), Base(300)]

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

// CHECK-NEXT: Base#1(100)
let basesConvertedToNSArray = bases as NSArray
print(basesConvertedToNSArray.object(at: 0) as! Base)
expectEqual(
"Base#1(100)",
String(basesConvertedToNSArray.object(at: 0) as! Base))

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

// Capture the representation of the first element
// CHECK-NEXT: [[base42:Base.*42]]
print(nsArrayOfBase.object(at: 0) as! Base)
let base42: ObjectIdentifier
do {
let b = nsArrayOfBase.object(at: 0) as! Base
expectEqual(42, b.value)
base42 = ObjectIdentifier(b)
}

// ...with the same elements
// CHECK-NEXT: [[base42]]
print(nsArrayOfBaseConvertedToAnyObjectArray[0] as! Base)
expectEqual(
base42,
ObjectIdentifier(nsArrayOfBaseConvertedToAnyObjectArray[0] as! Base))

// Verify that NSArray class methods are inherited by a Swift bridging class.
// CHECK-NEXT: Swift.{{.*}}Array
debugPrint(basesConvertedToNSArray.dynamicType)
// CHECK-NEXT: true
print(basesConvertedToNSArray.dynamicType.supportsSecureCoding)
let className = String(reflecting: basesConvertedToNSArray.dynamicType)
expectTrue(className.hasPrefix("Swift._ContiguousArrayStorage"))
expectTrue(basesConvertedToNSArray.dynamicType.supportsSecureCoding)

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

// upcast is implicit
let derivedAsBases: [Base] = derived

// CHECK-NEXT: [[derived0]]
print(derivedAsBases)
expectEqual(derived.count, derivedAsBases.count)
for (x, y) in zip(derived, derivedAsBases) {
expectTrue(x === y)
}

// Arrays are logically distinct after upcast
derived[0] = Derived(33)

// CHECK-NEXT: {{\[Derived#[0-9]+\(33\), Derived#[0-9]+\(22\)]}}
print(derived)
// CHECK-NEXT: [[derived0]]
print(derivedAsBases)

// CHECK-NEXT: [[derived0]]
if let roundTripDerived = derivedAsBases as? [Derived] {
print(roundTripDerived)
}
else {
print("roundTripDerived upcast failed")
}
expectEqual([Derived(33), Derived(22)], derived)
expectEqual([Derived(11), Derived(22)], derivedAsBases)

expectEqual(derived0, derivedAsBases as! [Derived])

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

// CHECK-NEXT: Explicit downcast-ability is based on element type, not buffer type
if let downcastBaseBuffer = derivedInBaseBuffer as? [Derived] {
print("Explicit downcast-ability is based on element type, not buffer type")
}
else {
print("Unexpected downcast failure")
}
// Explicit downcast-ability is based on element type, not buffer type
expectNotEmpty(derivedInBaseBuffer as? [Derived])

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

// CHECK-NEXT: downcastBackToBase = [[derived2]]
if let downcastBackToBase = derivedAsAnyObjectArray as? [Base] {
print("downcastBackToBase = \(downcastBackToBase)")
}
else {
print("downcastBackToBase failed")
}
let downcastBackToBase = derivedAsAnyObjectArray as? [Base]
expectNotEmpty(downcastBackToBase)

// CHECK-NEXT: downcastBackToDerived = [[derived2]]
if let downcastBackToDerived = derivedAsAnyObjectArray as? [Derived] {
print("downcastBackToDerived = \(downcastBackToDerived)")
}
else {
print("downcastBackToDerived failed")
if let downcastBackToDerived = expectNotEmpty(derivedAsAnyObjectArray as? [Derived]) {
expectEqual(derived2, downcastBackToDerived)
}

// CHECK-NEXT: downcastToProtocols = [[derived2]]
if let downcastToProtocols = derivedAsAnyObjectArray as? [Fooable] {
print("downcastToProtocols = \(downcastToProtocols)")
} else {
print("downcastToProtocols failed")
if let downcastToProtocols = expectNotEmpty(derivedAsAnyObjectArray as? [Fooable]) {
expectEqual(derived2, downcastToProtocols.map { $0 as! Derived })
}

// CHECK-NEXT: downcastToProtocols = [[derived2]]
if let downcastToProtocols = derivedAsAnyObjectArray as? [Barable] {
print("downcastToProtocols = \(downcastToProtocols)")
} else {
print("downcastToProtocols failed")
if let downcastToProtocols = expectNotEmpty(derivedAsAnyObjectArray as? [Barable]) {
expectEqual(derived2, downcastToProtocols.map { $0 as! Derived })
}

// CHECK-NEXT: downcastToProtocols = [[derived2]]
if let downcastToProtocols = derivedAsAnyObjectArray as? [protocol<Barable, Fooable>] {
print("downcastToProtocols = \(downcastToProtocols)")
} else {
print("downcastToProtocols failed")
if let downcastToProtocols = expectNotEmpty(derivedAsAnyObjectArray as? [protocol<Barable, Fooable>]) {
expectEqual(derived2, downcastToProtocols.map { $0 as! Derived })
}

// CHECK-NEXT: downcastToProtocols failed
if let downcastToProtocols = derivedAsAnyObjectArray as? [protocol<Barable, Bazable>] {
print("downcastToProtocols = \(downcastToProtocols)")
} else {
print("downcastToProtocols failed")
}
expectEmpty(derivedAsAnyObjectArray as? [protocol<Barable, Bazable>])
}

func doTestBridgedObjC() {
// CHECK: doTestBridgedObjC
print("doTestBridgedObjC")

testBridgedObjC(Thunks())
// 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)])
testBridgedObjC(Thunks())
// CHECK-NEXT: 5 elements in the array
Expand All @@ -329,7 +302,7 @@ func testBridgedVerbatim() {

// 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)])
}
testBridgedVerbatim()
doTestBridgedObjC()

//===--- Explicitly Bridged -----------------------------------------------===//
// BridgedSwift conforms to _ObjectiveCBridgeable
Expand Down Expand Up @@ -454,7 +427,7 @@ func testExplicitlyBridged() {

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

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

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

// CHECK-NEXT: ---Passed array---
print("---Passed array---")
let result = array as! [BridgedSwift]
// CHECK-NEXT: bridge operations (from, to) = (0, 0)
BridgedSwift.printStats()

expectEqual(0, bridgeFromOperationCount)
expectEqual(0, bridgeToOperationCount)

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

BridgedSwift.resetStats()
test.call(array as NSArray)
_ = test.call(array as NSArray)

// CHECK-NEXT: ---Returned Array---
print("---Returned Array---")
// CHECK-NEXT: bridge operations (from, to) = (0, 0)
BridgedSwift.printStats()
expectEqual(0, bridgeFromOperationCount)
expectEqual(0, bridgeToOperationCount)
}
testRoundTrip()
//===--- Non-bridging -----------------------------------------------------===//
Expand All @@ -566,5 +534,4 @@ func testMutableArray() {
}
testMutableArray()

// CHECK-NEXT: done.
print("done.")
runAllTests()
4 changes: 3 additions & 1 deletion test/1_stdlib/Inputs/DictionaryKeyValueTypesObjC.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ func isCocoaNSDictionary(_ d: NSDictionary) -> Bool {

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

var _objcKeyCount = _stdlib_AtomicInt(0)
Expand Down
16 changes: 16 additions & 0 deletions test/Interpreter/SDK/objc_fast_enumeration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,19 @@ var nsa2 = (a2._buffer._asCocoaArray() as AnyObject) as! NSArray
for x: AnyObject in nsa2 {
print(x.description!)
}

class X : CustomStringConvertible {
init(_ value: Int) { self.value = value }
var value: Int
var description: String { return "X(\(value))" }
}

// Enumeration over a _ContiguousArrayBuffer
// CHECK: X(3)
// CHECK: X(2)
// CHECK: X(1)
var a3 = [X(3), X(2), X(1)]
var nsa3 = (a3._buffer._asCocoaArray() as AnyObject) as! NSArray
for x: AnyObject in nsa3 {
print(x.description!)
}
7 changes: 4 additions & 3 deletions validation-test/stdlib/ArrayNew.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ ArrayTestSuite.test("BridgedFromObjC.Verbatim.ArrayIsCopied") {
ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.ArrayIsCopied") {
let source = [ 10, 20, 30 ]
let nsa = getAsNSMutableArray(source)
var result = nsa as! Array<TestBridgedValueTy>
var result = nsa as AnyObject as! Array<TestBridgedValueTy>
expectTrue(isNativeArray(result))

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

ArrayTestSuite.test("BridgedToObjC/Verbatim/BridgeBack/Adopt") {
// Bridge back to native array.
var native: [TestObjCValueTy] = convertNSArrayToArray(
getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 3))
var native: [TestObjCValueTy] =
getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 3) as! Array

let identity1 = unsafeBitCast(native, to: UInt.self)

// Mutate elements, but don't change count.
Expand Down

0 comments on commit b21d8be

Please sign in to comment.