// RUN: rm -rf %t // RUN: mkdir -p %t // // RUN: %S/../../utils/gyb %s -o %t/main.swift // RUN: %target-clang -fobjc-arc %S/Inputs/SlurpFastEnumeration/SlurpFastEnumeration.m -c -o %t/SlurpFastEnumeration.o // RUN: %S/../../utils/line-directive %t/main.swift -- %target-build-swift %S/Inputs/DictionaryKeyValueTypes.swift %S/Inputs/DictionaryKeyValueTypesObjC.swift %t/main.swift -I %S/Inputs/SlurpFastEnumeration/ -Xlinker %t/SlurpFastEnumeration.o -o %t/Array -Xfrontend -disable-access-control // RUN: %S/../../utils/line-directive %t/main.swift -- %target-run %t/Array // REQUIRES: executable_test // XFAIL: linux import Darwin import StdlibUnittest import StdlibCollectionUnittest // Also import modules which are used by StdlibUnittest internally. This // workaround is needed to link all required libraries in case we compile // StdlibUnittest with -sil-serialize-all. import SwiftPrivate #if _runtime(_ObjC) import ObjectiveC #endif import StdlibUnittestFoundationExtras import Foundation %{ all_array_types = [ 'ContiguousArray', 'ArraySlice', 'Array' ] }% extension Array { var identity: UnsafePointer { return self._buffer.identity } } extension ArraySlice { var identity: UnsafePointer { return self._buffer.identity } } extension ContiguousArray { var identity: UnsafePointer { return self._buffer.identity } } var ArrayTestSuite = TestSuite("Array") ArrayTestSuite.test("sizeof") { var a = [ 10, 20, 30 ] #if arch(i386) || arch(arm) expectEqual(4, sizeofValue(a)) #else expectEqual(8, sizeofValue(a)) #endif } ArrayTestSuite.test("valueDestruction") { var a = [TestValueTy]() for i in 100...110 { a.append(TestValueTy(i)) } } //===----------------------------------------------------------------------===// // Native array tests // FIXME: incomplete. //===----------------------------------------------------------------------===// ArrayTestSuite.test("Native/count/empty") { let a = [TestValueTy]() expectEqual(0, a.count) } ArrayTestSuite.test("Native/count") { let a = [ TestValueTy(10), TestValueTy(20), TestValueTy(30) ] expectEqual(3, a.count) } ArrayTestSuite.test("Native/isEmpty/empty") { let a = [TestValueTy]() expectTrue(a.isEmpty) } ArrayTestSuite.test("Native/isEmpty") { let a = [ TestValueTy(10), TestValueTy(20), TestValueTy(30) ] expectFalse(a.isEmpty) } protocol TestProtocol1 {} % for array_type in all_array_types: // Check that the generic parameter is called 'Element'. extension ${array_type} where Element : TestProtocol1 { var _elementIsTestProtocol1: Bool { fatalError("not implemented") } } /// Returns an ${array_type} that does not share its buffer with other arrays. func getFresh${array_type}(sequence: S) -> ${array_type} { var result: ${array_type} = [] result.reserveCapacity(sequence.underestimatedCount) for element in sequence { result.append(element) } return result } % end enum EnumWithoutPayloads : Equatable { case A, B, C, D } func == (lhs: EnumWithoutPayloads, rhs: EnumWithoutPayloads) -> Bool { switch (lhs, rhs) { case (.A, .A), (.B, .B), (.C, .C), (.D, .D): return true default: return false } } struct SequenceWithCustomUnderestimatedCount : Sequence { init(_ data: [Int]) { self._data = MinimalSequence(elements: data.map(OpaqueValue.init)) } func makeIterator() -> MinimalSequence>.Iterator { return _data.makeIterator() } var underestimatedCount: Int { SequenceWithCustomUnderestimatedCount.timesUnderestimatedCountWasCalled += 1 return _data.underestimatedCount } static var timesUnderestimatedCountWasCalled: Int = 0 let _data: MinimalSequence> } % for array_type in all_array_types: ArrayTestSuite.test("${array_type}/init(Sequence)") { let base = SequenceWithCustomUnderestimatedCount( [ 0, 30, 10, 90 ]) SequenceWithCustomUnderestimatedCount.timesUnderestimatedCountWasCalled = 0 let result = ${array_type}(base) expectEqual([ 0, 30, 10, 90 ], result.map { $0.value }) expectEqual(1, SequenceWithCustomUnderestimatedCount.timesUnderestimatedCountWasCalled) expectEqualSequence( [], Array(base).map { $0.value }, "sequence should be consumed") } ArrayTestSuite.test("${array_type}/Sliceable/Enums") { typealias E = EnumWithoutPayloads do { let expected = [ E.A, E.B, E.C, E.D ] let sliceable = ${array_type}(expected) checkSliceableWithBidirectionalIndex(expected, sliceable) } /* FIXME: add this test when Array can be conditionally Equatable. do { let expected = [ [ E.A, E.B ], [ E.B, E.C ], [ E.D ], [ E.A, E.B, E.D ] ] let sliceable = ${array_type}(expected) checkSliceableWithBidirectionalIndex( expected, sliceable, SourceLocStack().withCurrentLoc()) } */ } ArrayTestSuite.test("${array_type}/appendNonUnique") { var x: ${array_type} = [] x.reserveCapacity(10002) let capacity = x.capacity for _ in 1...10000 { let y = x x.append(1) expectTrue(x.capacity == capacity) let z = x x.remove(at: 0) } } ArrayTestSuite.test("${array_type}/emptyAllocation") { let arr0 = ${array_type}() let arr1 = ${array_type}(repeating: TestValueTy(0), count: 0) // Empty arrays all use the same buffer expectEqual(arr0._buffer.identity, arr1._buffer.identity) let hasNilBuffer = arr1.identity == nil expectFalse(hasNilBuffer) let arr2: ${array_type} = [] let emptyLiteralsShareBuffer = arr0._buffer.identity == arr2._buffer.identity expectTrue(emptyLiteralsShareBuffer) } ArrayTestSuite.test("${array_type}/filter") { do { let arr: ${array_type} = [] var result = arr.filter() { (x: Int) -> Bool in expectUnreachable() return true } expectType(Array.self, &result) expectEqual([], result) expectEqual(0, result.capacity) } do { let arr: ${array_type} = [ 0, 30, 10, 90 ] let result = arr.filter() { (x: Int) -> Bool in true } expectEqual([ 0, 30, 10, 90 ], result) expectGE(2 * result.count, result.capacity) } do { let arr: ${array_type} = [ 0, 30, 10, 90 ] let result = arr.filter() { (x: Int) -> Bool in false } expectEqual([], result) expectGE(2 * result.count, result.capacity) } do { let arr: ${array_type} = [ 0, 30, 10, 90 ] let result = arr.filter() { $0 % 3 == 0 } expectEqual([ 0, 30, 90 ], result) expectGE(2 * result.count, result.capacity) } } ArrayTestSuite.test("${array_type}/map") { do { let arr: ${array_type} = [] var result = arr.map() { (x: Int) -> Int16 in expectUnreachable() return 42 } expectType(Array.self, &result) expectEqual([], result) expectEqual(0, result.capacity) } do { let arr: ${array_type} = [ 0, 30, 10, 90 ] let result = arr.map() { $0 + 1 } expectEqual([ 1, 31, 11, 91 ], result) expectGE(2 * result.count, result.capacity) } } ArrayTestSuite.test("${array_type}/flatMap") { let enumerate : Int -> ${array_type} = { return ${array_type}(1..<($0 + 1)) } expectEqualSequence([], ${array_type}().flatMap(enumerate)) expectEqualSequence([ 1 ], ${array_type}([ 1 ]).flatMap(enumerate)) expectEqualSequence( [ 1, 1, 2 ], ${array_type}([ 1, 2 ]).flatMap(enumerate)) expectEqualSequence( [ 1, 1, 1, 2 ], ${array_type}([ 1, 2 ]).flatMap(enumerate).flatMap(enumerate)) } ArrayTestSuite.test("${array_type}/Mirror") { do { let input: ${array_type} = [] var output = "" dump(input, to: &output) let expected = "- 0 elements\n" expectEqual(expected, output) } do { let input: ${array_type} = [ 10, 20, 30, 40 ] var output = "" dump(input, to: &output) let expected = "▿ 4 elements\n" + " - 10\n" + " - 20\n" + " - 30\n" + " - 40\n" expectEqual(expected, output) } % if array_type == 'ArraySlice': do { let base = [ 10, 20, 30, 40 ] let input: ArraySlice = base[1..<3] var output = "" dump(input, to: &output) let expected = "▿ 2 elements\n" + " - 20\n" + " - 30\n" expectEqual(expected, output) } % end } % end % for Kind in ['Array', 'ContiguousArray']: ArrayTestSuite.test("${Kind}/popLast") { // Empty do { var a = ${Kind}() let popped = a.popLast() expectEmpty(popped) expectTrue(a.isEmpty) } do { var popped = [Int]() var a: ${Kind} = [1010, 2020, 3030] while let element = a.popLast() { popped.append(element) } expectEqualSequence([1010, 2020, 3030], popped.reversed()) expectTrue(a.isEmpty) } } % end // Check how removeFirst() affects indices. % for Kind in ['Array', 'ContiguousArray']: ArrayTestSuite.test("${Kind}/removeFirst") { do { var a: ${Kind}> = ${Kind}([ 1 ].map(OpaqueValue.init)) a.removeFirst() expectEqual(0, a.startIndex) } do { var a: ${Kind}> = ${Kind}([ 1, 2 ].map(OpaqueValue.init)) a.removeFirst() expectEqual(0, a.startIndex) } } % end ArrayTestSuite.test("ArraySlice/removeFirst") { do { let a: [OpaqueValue] = [ 99, 1010, 99 ].map(OpaqueValue.init) var s = a[1..<2] expectEqual(1, s.startIndex) s.removeFirst() expectEqual(2, s.startIndex) } do { let a: [OpaqueValue] = [ 99, 1010, 2020, 99 ].map(OpaqueValue.init) var s = a[1..<2] expectEqual(1, s.startIndex) s.removeFirst() expectEqual(2, s.startIndex) } } //===----------------------------------------------------------------------===// // _withUnsafeMutableBufferPointerIfSupported() //===----------------------------------------------------------------------===// struct WithUnsafeMutableBufferPointerIfSupportedTest { let sequence: [Int] let loc: SourceLoc init( _ sequence: [Int], file: String = #file, line: UInt = #line ) { self.sequence = sequence self.loc = SourceLoc(file, line, comment: "test data") } } let withUnsafeMutableBufferPointerIfSupportedTests = [ WithUnsafeMutableBufferPointerIfSupportedTest([]), WithUnsafeMutableBufferPointerIfSupportedTest([ 10 ]), WithUnsafeMutableBufferPointerIfSupportedTest([ 10, 20, 30, 40, 50 ]), ] % for array_type in all_array_types: ArrayTestSuite.test("${array_type}/_withUnsafeMutableBufferPointerIfSupported") { for test in withUnsafeMutableBufferPointerIfSupportedTests { var a = getFresh${array_type}(test.sequence.map(OpaqueValue.init)) do { // Read. var result = a._withUnsafeMutableBufferPointerIfSupported { (baseAddress, count) -> OpaqueValue<[OpaqueValue]> in let bufferPointer = UnsafeMutableBufferPointer(start: baseAddress, count: count) return OpaqueValue(Array(bufferPointer)) } expectType(Optional>>>.self, &result) expectEqualSequence(test.sequence, result!.value.map { $0.value }) expectEqualSequence(test.sequence, a.map { $0.value }) } do { // Read and write. var result = a._withUnsafeMutableBufferPointerIfSupported { (baseAddress, count) -> OpaqueValue>> in let bufferPointer = UnsafeMutableBufferPointer(start: baseAddress, count: count) let result = OpaqueValue(Array(bufferPointer)) for i in bufferPointer.indices { bufferPointer[i] = OpaqueValue(bufferPointer[i].value * 10) } return result } expectType(Optional>>>.self, &result) expectEqualSequence(test.sequence, result!.value.map { $0.value }) expectEqualSequence( test.sequence.map { $0 * 10 }, a.map { $0.value }) } } // FIXME: tests for arrays bridged from Objective-C. } ArrayTestSuite.test("${array_type}/_withUnsafeMutableBufferPointerIfSupported/ReplacingTheBufferTraps/1") { var a = getFresh${array_type}([ OpaqueValue(10) ]) var result = a._withUnsafeMutableBufferPointerIfSupported { (baseAddress, count) -> OpaqueValue in // buffer = UnsafeMutableBufferPointer(start: buffer.baseAddress, count: 0) // FIXME: does not trap since the buffer is not passed inout. // expectCrashLater() return OpaqueValue(42) } } ArrayTestSuite.test("${array_type}/_withUnsafeMutableBufferPointerIfSupported/ReplacingTheBufferTraps/2") { var a = getFresh${array_type}([ OpaqueValue(10) ]) var result = a._withUnsafeMutableBufferPointerIfSupported { (baseAddress, count) -> OpaqueValue in // buffer = UnsafeMutableBufferPointer(start: nil, count: 1) // FIXME: does not trap since the buffer is not passed inout. // expectCrashLater() return OpaqueValue(42) } } //===--- // Check that iterators traverse a snapshot of the collection. //===--- ArrayTestSuite.test( "${array_type}/mutationDoesNotAffectIterator/subscript/store") { var arr: ${array_type} = [ 1010, 1020, 1030 ] var iter = arr.makeIterator() arr[0] = 1011 expectEqual([ 1010, 1020, 1030 ], Array(IteratorSequence(iter))) } ArrayTestSuite.test( "${array_type}/mutationDoesNotAffectIterator/subscript/append") { var arr: ${array_type} = [ 1010, 1020, 1030 ] var iter = arr.makeIterator() arr.append(1040) expectEqual([ 1010, 1020, 1030 ], Array(IteratorSequence(iter))) } ArrayTestSuite.test( "${array_type}/mutationDoesNotAffectIterator/subscript/replaceSubrange") { var arr: ${array_type} = [ 1010, 1020, 1030 ] var iter = arr.makeIterator() arr.replaceSubrange(1..<3, with: [ 1040, 1050, 1060 ]) expectEqual([ 1010, 1020, 1030 ], Array(IteratorSequence(iter))) } % end //===----------------------------------------------------------------------===// // COW tests // FIXME: incomplete. //===----------------------------------------------------------------------===// func withInoutInt(x: inout Int, body: (x: inout Int) -> Void) { body(x: &x) } func withInoutT(x: inout T, body: (x: inout T) -> Void) { body(x: &x) } % for element_type in [ 'TestValueTy', 'TestBridgedValueTy' ]: % for array_type in all_array_types: ArrayTestSuite.test("${array_type}<${element_type}>/subscript(_: Int)/COW") { var a: ${array_type}<${array_type}<${element_type}>> = [ [ ${element_type}(10), ${element_type}(20), ${element_type}(30), ${element_type}(40), ${element_type}(50), ${element_type}(60), ${element_type}(70) ] ] let identityOuter = a.identity var identityInner = a[0].identity func checkIdentity(stackTrace: SourceLocStack) { % if element_type == 'TestValueTy': // Does not reallocate storage because we changed a property based on a // reference; array storage was not changed. Writeback of the inner array // does not happen. expectEqual(identityOuter, a.identity, stackTrace: stackTrace) expectEqual(identityInner, a[0].identity, stackTrace: stackTrace) % else: expectEqual(identityOuter, a.identity, stackTrace: stackTrace) // Should not reallocate storage. expectEqual(identityInner, a[0].identity, stackTrace: stackTrace) % end } // Mutating through a subscript expression. a[0][0] = ${element_type}(1010) checkIdentity(SourceLocStack().withCurrentLoc()) a[0][1].value = 1020 checkIdentity(SourceLocStack().withCurrentLoc()) withInoutT(&a) { (x: inout ${array_type}<${array_type}<${element_type}>>) in x[0][2].value += 1000 } checkIdentity(SourceLocStack().withCurrentLoc()) withInoutT(&a[0]) { (x: inout ${array_type}<${element_type}>) in x[3].value += 1000 } checkIdentity(SourceLocStack().withCurrentLoc()) // This will reallocate storage unless Array uses addressors for subscript. //withInoutT(&a[0][4]) { // (x: inout ${element_type}) in // x.value += 1000 //} // FIXME: both of these lines crash the compiler. // Passing an expression based on addressors as // 'inout' crashes SILGen //withInoutT(&a[0][5].value, { $0 += 1000 }) //withInoutInt(&a[0][6].value, { $0 += 1000 }) // Don't change the last element. expectEqual(1010, a[0][0].value) expectEqual(1020, a[0][1].value) expectEqual(1030, a[0][2].value) expectEqual(1040, a[0][3].value) expectEqual(50, a[0][4].value) expectEqual(60, a[0][5].value) expectEqual(70, a[0][6].value) } ArrayTestSuite.test("${array_type}<${element_type}>/subscript(_: Range)/COW") { var a: ${array_type}<${array_type}<${element_type}>> = [ [ ${element_type}(10), ${element_type}(20), ${element_type}(30), ${element_type}(40), ${element_type}(50), ${element_type}(60), ${element_type}(70), ${element_type}(80), ${element_type}(90), ] ] let identityOuter = a.identity var identityInner = a[0].identity func checkIdentity(stackTrace: SourceLocStack) { % if element_type == 'TestValueTy': // Does not reallocate storage because we changed a property based on a // reference; array storage was not changed. expectEqual(identityOuter, a.identity, stackTrace: stackTrace) expectEqual(identityInner, a[0].identity, stackTrace: stackTrace) % else: expectEqual(identityOuter, a.identity, stackTrace: stackTrace) // Writeback happens in subscript(Range), but array notices that the new // value did not change. // Another writeback happens in Array.subscript(Int), but this is not what we // want. expectNotEqual(identityInner, a[0].identity, stackTrace: stackTrace) identityInner = a[0].identity % end } // Mutating through a subscript expression. a[0..<1][0][0] = ${element_type}(1010) // Reallocates storage because of the writeback in Array.subscript(Int). expectEqual(identityOuter, a.identity) expectNotEqual(identityInner, a[0].identity) identityInner = a[0].identity a[0..<1][0][1].value = 1020 checkIdentity(SourceLocStack().withCurrentLoc()) withInoutT(&a) { (x: inout ${array_type}<${array_type}<${element_type}>>) in x[0..<1][0][2].value += 1000 } checkIdentity(SourceLocStack().withCurrentLoc()) withInoutT(&a[0..<1]) { (x: inout ArraySlice<${array_type}<${element_type}>>) in x[0][3].value += 1000 } checkIdentity(SourceLocStack().withCurrentLoc()) withInoutT(&a[0..<1][0]) { (x: inout ${array_type}<${element_type}>) in x[4].value += 1000 } checkIdentity(SourceLocStack().withCurrentLoc()) withInoutT(&a[0..<1][0][5]) { (x: inout ${element_type}) in x.value += 1000 } // Reallocates storage because of the writeback in Array.subscript(Int) // (writeback is being requested for the array element even though it is not // needed). expectEqual(identityOuter, a.identity) expectNotEqual(identityInner, a[0].identity) identityInner = a[0].identity withInoutT(&a[0..<1][0][6].value) { (x: inout Int) in x += 1000 } checkIdentity(SourceLocStack().withCurrentLoc()) withInoutInt(&a[0..<1][0][7].value) { (x: inout Int) in x += 1000 } checkIdentity(SourceLocStack().withCurrentLoc()) // Don't change the last element. expectEqual(1010, a[0][0].value) expectEqual(1020, a[0][1].value) expectEqual(1030, a[0][2].value) expectEqual(1040, a[0][3].value) expectEqual(1050, a[0][4].value) expectEqual(1060, a[0][5].value) expectEqual(1070, a[0][6].value) expectEqual(1080, a[0][7].value) expectEqual(90, a[0][8].value) } % end % end // FIXME: all the tests below are applicable to ArraySlice, too. //===----------------------------------------------------------------------===// // NSArray -> Array bridging tests // FIXME: incomplete. //===----------------------------------------------------------------------===// func isNativeArray(a: Array) -> Bool { return a._hoistableIsNativeTypeChecked() } func isCocoaArray(a: Array) -> Bool { return !isNativeArray(a) } func getAsImmutableNSArray(a: Array) -> NSArray { var elements = a.map { TestObjCValueTy($0) as AnyObject? } return NSArray(objects: &elements, count: elements.count) } func getAsNSArray(a: Array) -> NSArray { // Return an `NSMutableArray` to make sure that it has a unique // pointer identity. return getAsNSMutableArray(a) } func getAsNSMutableArray(a: Array) -> NSMutableArray { let result = NSMutableArray() for element in a { result.add(TestObjCValueTy(element)) } return result } @objc class CustomImmutableNSArray : NSArray { init(_privateInit: ()) { super.init() } override init() { expectUnreachable() super.init() } override init(objects: UnsafePointer, count: Int) { super.init(objects: objects, count: count) } required init(coder aDecoder: NSCoder) { fatalError("init(coder:) not implemented by CustomImmutableNSArray") } @objc(copyWithZone:) override func copy(with zone: NSZone) -> AnyObject { CustomImmutableNSArray.timesCopyWithZoneWasCalled += 1 return self } @objc override func object(at index: Int) -> AnyObject { CustomImmutableNSArray.timesObjectAtIndexWasCalled += 1 return _data[index] } @objc override var count: Int { CustomImmutableNSArray.timesCountWasCalled += 1 return _data.count } @objc override func countByEnumerating( with state: UnsafeMutablePointer, objects: AutoreleasingUnsafeMutablePointer, count: Int ) -> Int { var theState = state.pointee if theState.state == 0 { theState.state = 1 theState.itemsPtr = AutoreleasingUnsafeMutablePointer(_data._baseAddressIfContiguous) theState.mutationsPtr = _fastEnumerationStorageMutationsPtr state.pointee = theState return _data.count } return 0 } let _data = [ 10, 20, 30 ].map { TestObjCValueTy($0) } static var timesCopyWithZoneWasCalled = 0 static var timesObjectAtIndexWasCalled = 0 static var timesCountWasCalled = 0 } ArrayTestSuite.test("BridgedFromObjC.Verbatim.BridgeUsingAs") { do { let source = [ 10, 20, 30 ] let nsa = getAsNSArray(source) var result = nsa as Array expectTrue(isCocoaArray(result)) expectType(Array.self, &result) checkSequence(source.map { TestObjCValueTy($0) as AnyObject }, result) { ($0 as! TestObjCValueTy).value == ($1 as! TestObjCValueTy).value } } do { let source = [ 10, 20, 30 ] let nsa = getAsNSArray(source) var result = nsa as! Array expectTrue(isCocoaArray(result)) expectType(Array.self, &result) checkSequence(source.map { TestObjCValueTy($0) }, result) { $0.value == $1.value } } } ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.BridgeUsingAs") { let source = [ 10, 20, 30 ] let nsa = getAsNSArray(source) var result = nsa as! Array expectTrue(isNativeArray(result)) expectType(Array.self, &result) checkSequence(source.map { TestBridgedValueTy($0) }, result) { $0.value == $1.value } } ArrayTestSuite.test("BridgedFromObjC.Verbatim.ArrayIsCopied") { let source = [ 10, 20, 30 ] let nsa = getAsNSMutableArray(source) let result = nsa as Array expectTrue(isCocoaArray(result)) // Delete the value from NSMutableArray. expectEqual(20, (nsa[1] as! TestObjCValueTy).value) nsa.removeObject(at: 1) expectEqual(30, (nsa[1] as! TestObjCValueTy).value) // Check that the Array is not affected. expectEqual(20, result[1].value) } ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.ArrayIsCopied") { let source = [ 10, 20, 30 ] let nsa = getAsNSMutableArray(source) var result = nsa as! Array expectTrue(isNativeArray(result)) // Delete the value from NSMutableArray. expectEqual(20, (nsa[1] as! TestObjCValueTy).value) nsa.removeObject(at: 1) expectEqual(30, (nsa[1] as! TestObjCValueTy).value) // Check that the Array is not affected. expectEqual(20, result[1].value) } ArrayTestSuite.test("BridgedFromObjC.Verbatim.NSArrayIsRetained") { let nsa = NSArray(array: getAsNSArray([ 10, 20, 30 ])) var a: Array = convertNSArrayToArray(nsa) var bridgedBack: NSArray = convertArrayToNSArray(a) expectEqual( unsafeBitCast(nsa, to: Int.self), unsafeBitCast(bridgedBack, to: Int.self)) _fixLifetime(nsa) _fixLifetime(a) _fixLifetime(bridgedBack) } ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.NSArrayIsCopied") { let nsa = NSArray(array: getAsNSArray([ 10, 20, 30 ])) var a: Array = convertNSArrayToArray(nsa) var bridgedBack: NSArray = convertArrayToNSArray(a) expectNotEqual( unsafeBitCast(nsa, to: Int.self), unsafeBitCast(bridgedBack, to: Int.self)) _fixLifetime(nsa) _fixLifetime(a) _fixLifetime(bridgedBack) } ArrayTestSuite.test("BridgedFromObjC.Verbatim.ImmutableArrayIsRetained") { let nsa: NSArray = CustomImmutableNSArray(_privateInit: ()) CustomImmutableNSArray.timesCopyWithZoneWasCalled = 0 CustomImmutableNSArray.timesObjectAtIndexWasCalled = 0 CustomImmutableNSArray.timesCountWasCalled = 0 let a: Array = convertNSArrayToArray(nsa) expectEqual(1, CustomImmutableNSArray.timesCopyWithZoneWasCalled) expectEqual(0, CustomImmutableNSArray.timesObjectAtIndexWasCalled) expectEqual(0, CustomImmutableNSArray.timesCountWasCalled) let bridgedBack: NSArray = convertArrayToNSArray(a) expectEqual( unsafeBitCast(nsa, to: Int.self), unsafeBitCast(bridgedBack, to: Int.self)) _fixLifetime(nsa) _fixLifetime(a) _fixLifetime(bridgedBack) } ArrayTestSuite.test("BridgedFromObjC.Nonverbatim.ImmutableArrayIsCopied") { let nsa: NSArray = CustomImmutableNSArray(_privateInit: ()) CustomImmutableNSArray.timesCopyWithZoneWasCalled = 0 CustomImmutableNSArray.timesObjectAtIndexWasCalled = 0 CustomImmutableNSArray.timesCountWasCalled = 0 TestBridgedValueTy.bridgeOperations = 0 var a: Array = [] // FIXME: bridging shouldn't dump array contents into the autorelease pool. autoreleasepoolIfUnoptimizedReturnAutoreleased { a = convertNSArrayToArray(nsa) expectEqual(1, CustomImmutableNSArray.timesCopyWithZoneWasCalled) expectEqual(3, CustomImmutableNSArray.timesObjectAtIndexWasCalled) expectNotEqual(0, CustomImmutableNSArray.timesCountWasCalled) expectEqual(3, TestBridgedValueTy.bridgeOperations) } let bridgedBack: NSArray = convertArrayToNSArray(a) expectNotEqual( unsafeBitCast(nsa, to: Int.self), unsafeBitCast(bridgedBack, to: Int.self)) _fixLifetime(nsa) _fixLifetime(a) _fixLifetime(bridgedBack) } // FIXME: test API calls on the BridgedFromObjC arrays. //===----------------------------------------------------------------------===// // Array -> NSArray bridging tests // // Element is bridged verbatim. // // FIXME: incomplete. //===----------------------------------------------------------------------===// ArrayTestSuite.test("BridgedToObjC.Verbatim.BridgeUsingAs") { let source = [ 10, 20, 30 ].map { TestObjCValueTy($0) } let result = source as NSArray expectTrue(isNativeNSArray(result)) expectEqual(3, result.count) autoreleasepoolIfUnoptimizedReturnAutoreleased { expectEqual(10, (result[0] as! TestObjCValueTy).value) expectEqual(20, (result[1] as! TestObjCValueTy).value) expectEqual(30, (result[2] as! TestObjCValueTy).value) } } ArrayTestSuite.test("BridgedToObjC/Verbatim/count/empty") { let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 0) expectEqual(0, a.count) } ArrayTestSuite.test("BridgedToObjC/Verbatim/count") { let a = getBridgedNSArrayOfRefTypeVerbatimBridged() expectEqual(3, a.count) } for index in [ -100, -1, 0, 1, 100 ] { ArrayTestSuite.test( "BridgedToObjC/Verbatim/objectAtIndex/empty/trap/\(index)") .crashOutputMatches("Array index out of range") .code { let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 0) expectCrashLater() a.object(at: index) } } for index in [ -100, -1, 3, 4, 100 ] { ArrayTestSuite.test("BridgedToObjC/Verbatim/objectAtIndex/trap/\(index)") .crashOutputMatches("Array index out of range") .code { let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 3) expectCrashLater() a.object(at: index) } } ArrayTestSuite.test("BridgedToObjC/Verbatim/objectAtIndex") { let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 3) var v: AnyObject = a.object(at: 0) expectEqual(10, (v as! TestObjCValueTy).value) let idValue0 = unsafeBitCast(v, to: UInt.self) v = a.object(at: 1) expectEqual(20, (v as! TestObjCValueTy).value) let idValue1 = unsafeBitCast(v, to: UInt.self) v = a.object(at: 2) expectEqual(30, (v as! TestObjCValueTy).value) let idValue2 = unsafeBitCast(v, to: UInt.self) for i in 0..<3 { expectEqual(idValue0, unsafeBitCast(a.object(at: 0), to: UInt.self)) expectEqual(idValue1, unsafeBitCast(a.object(at: 1), to: UInt.self)) expectEqual(idValue2, unsafeBitCast(a.object(at: 2), to: UInt.self)) } expectAutoreleasedKeysAndValues(unopt: (0, 3)) } for indexRange in [ -2..<(-2), 1..<1, 0..<4, -2..<(-1), -1..<2, 0..<1, 2..<4, 4..<5 ] as [Range] { ArrayTestSuite.test("BridgedToObjC/Verbatim/getObjects/empty/trap/\(indexRange)") .crashOutputMatches("Array index out of range") .code { let a = getBridgedNSArrayOfRefTypeVerbatimBridged( numElements: 0, capacity: 16) let buffer = UnsafeMutablePointer(allocatingCapacity: 16) a.getObjects( AutoreleasingUnsafeMutablePointer(buffer), range: NSRange(0..<0)) expectCrashLater() a.getObjects( AutoreleasingUnsafeMutablePointer(buffer), range: NSRange(indexRange)) } } for indexRange in [ 0..<4, -2..<(-1), -1..<2, 2..<4, 4..<5 ] as [Range] { ArrayTestSuite.test("BridgedToObjC/Verbatim/getObjects/trap/\(indexRange)") .crashOutputMatches("Array index out of range") .code { let a = getBridgedNSArrayOfRefTypeVerbatimBridged( numElements: 3, capacity: 16) let buffer = UnsafeMutablePointer(allocatingCapacity: 16) a.getObjects( AutoreleasingUnsafeMutablePointer(buffer), range: NSRange(0..<3)) expectCrashLater() a.getObjects( AutoreleasingUnsafeMutablePointer(buffer), range: NSRange(indexRange)) } } ArrayTestSuite.test("BridgedToObjC/Verbatim/getObjects") { let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 3) let buffer = UnsafeMutablePointer(allocatingCapacity: 16) a.getObjects( AutoreleasingUnsafeMutablePointer(buffer), range: NSRange(0..<3)) var v: AnyObject = buffer[0] expectEqual(10, (v as! TestObjCValueTy).value) let idValue0 = unsafeBitCast(v, to: UInt.self) v = buffer[1] expectEqual(20, (v as! TestObjCValueTy).value) let idValue1 = unsafeBitCast(v, to: UInt.self) v = buffer[2] expectEqual(30, (v as! TestObjCValueTy).value) let idValue2 = unsafeBitCast(v, to: UInt.self) for i in 0..<3 { expectEqual(idValue0, unsafeBitCast(a.object(at: 0), to: UInt.self)) expectEqual(idValue1, unsafeBitCast(a.object(at: 1), to: UInt.self)) expectEqual(idValue2, unsafeBitCast(a.object(at: 2), to: UInt.self)) } buffer.deallocateCapacity(3) _fixLifetime(a) expectAutoreleasedKeysAndValues(unopt: (0, 3)) } ArrayTestSuite.test("BridgedToObjC/Verbatim/copyWithZone") { let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 3) let copy: AnyObject = a.copy(with: nil) expectEqual( unsafeBitCast(a, to: UInt.self), unsafeBitCast(copy, to: UInt.self)) } ArrayTestSuite.test("BridgedToObjC/Verbatim/FastEnumeration/UseFromSwift/Empty") { let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 0) checkArrayFastEnumerationFromSwift( [], a, { a }, { ($0 as! TestObjCValueTy).value }) } ArrayTestSuite.test("BridgedToObjC/Verbatim/FastEnumeration/UseFromSwift/3") { let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 3) checkArrayFastEnumerationFromSwift( [ 10, 20, 30 ], a, { a }, { ($0 as! TestObjCValueTy).value }) } ArrayTestSuite.test("BridgedToObjC/Verbatim/FastEnumeration/UseFromSwift/7") { let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 7) checkArrayFastEnumerationFromSwift( [ 10, 20, 30, 40, 50, 60, 70 ], a, { a }, { ($0 as! TestObjCValueTy).value }) } ArrayTestSuite.test("BridgedToObjC/Verbatim/FastEnumeration/UseFromObjC/Empty") { let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 0) checkArrayFastEnumerationFromObjC( [], a, { a }, { ($0 as! TestObjCValueTy).value }) } ArrayTestSuite.test("BridgedToObjC/Verbatim/FastEnumeration/UseFromObjC") { let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 3) checkArrayFastEnumerationFromObjC( [ 10, 20, 30 ], a, { a }, { ($0 as! TestObjCValueTy).value }) } ArrayTestSuite.test("BridgedToObjC/Verbatim/ObjectEnumerator/FastEnumeration/UseFromSwift/Empty") { let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 0) checkArrayFastEnumerationFromSwift( [], a, { a.objectEnumerator() }, { ($0 as! TestObjCValueTy).value }) } ArrayTestSuite.test("BridgedToObjC/Verbatim/ObjectEnumerator/FastEnumeration/UseFromSwift") { let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 3) checkArrayFastEnumerationFromSwift( [ 10, 20, 30 ], a, { a.objectEnumerator() }, { ($0 as! TestObjCValueTy).value }) } ArrayTestSuite.test("BridgedToObjC/Verbatim/ObjectEnumerator/FastEnumeration/UseFromSwift/Partial") { let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 9) checkArrayEnumeratorPartialFastEnumerationFromSwift( [ 10, 20, 30, 40, 50, 60, 70, 80, 90 ], a, maxFastEnumerationItems: 5, { ($0 as! TestObjCValueTy).value }) } ArrayTestSuite.test("BridgedToObjC/Verbatim/ObjectEnumerator/FastEnumeration/UseFromObjC") { let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 3) checkArrayFastEnumerationFromObjC( [ 10, 20, 30 ], a, { a.objectEnumerator() }, { ($0 as! TestObjCValueTy).value }) } ArrayTestSuite.test("BridgedToObjC/Verbatim/BridgeBack/Reallocate") { let a = getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 3) var v: AnyObject = a[0] expectEqual(10, (v as! TestObjCValueTy).value) let idValue0 = unsafeBitCast(v, to: UInt.self) v = a[1] expectEqual(20, (v as! TestObjCValueTy).value) let idValue1 = unsafeBitCast(v, to: UInt.self) v = a[2] expectEqual(30, (v as! TestObjCValueTy).value) let idValue2 = unsafeBitCast(v, to: UInt.self) // Bridge back to native array. var native: [TestObjCValueTy] = convertNSArrayToArray(a) native[0] = TestObjCValueTy(110) native[1] = TestObjCValueTy(120) native[2] = TestObjCValueTy(130) native.append(TestObjCValueTy(140)) // Ensure that the compiler does not elide mutation of the native array. _blackHole(native) // Check that mutating the native array did not affect the bridged array. expectEqual(3, a.count) expectEqual(idValue0, unsafeBitCast(a.object(at: 0), to: UInt.self)) expectEqual(idValue1, unsafeBitCast(a.object(at: 1), to: UInt.self)) expectEqual(idValue2, unsafeBitCast(a.object(at: 2), to: UInt.self)) expectAutoreleasedKeysAndValues(unopt: (0, 3)) } ArrayTestSuite.test("BridgedToObjC/Verbatim/BridgeBack/Adopt") { // Bridge back to native array. var native: [TestObjCValueTy] = convertNSArrayToArray( getBridgedNSArrayOfRefTypeVerbatimBridged(numElements: 3)) let identity1 = unsafeBitCast(native, to: UInt.self) // Mutate elements, but don't change count. native[0] = TestObjCValueTy(110) native[1] = TestObjCValueTy(120) native[2] = TestObjCValueTy(130) // Expect no reallocations. expectEqual(identity1, unsafeBitCast(native, to: UInt.self)) } //===----------------------------------------------------------------------===// // Array -> NSArray bridging tests // // Element is bridged non-verbatim. // // FIXME: incomplete. //===----------------------------------------------------------------------===// ArrayTestSuite.test("BridgedToObjC.Nonverbatim.BridgeUsingAs") { let source = [ 10, 20, 30 ].map { TestBridgedValueTy($0) } var result = source as NSArray expectTrue(isNativeNSArray(result)) expectEqual(3, result.count) autoreleasepoolIfUnoptimizedReturnAutoreleased { expectEqual(10, (result[0] as! TestBridgedValueTy).value) expectEqual(20, (result[1] as! TestBridgedValueTy).value) expectEqual(30, (result[2] as! TestBridgedValueTy).value) } } ArrayTestSuite.test("BridgedToObjC/Custom/count/empty") { let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 0) expectEqual(0, a.count) expectEqual(0, TestBridgedValueTy.bridgeOperations) } ArrayTestSuite.test("BridgedToObjC/Custom/count") { let a = getBridgedNSArrayOfValueTypeCustomBridged() expectEqual(3, a.count) expectEqual(0, TestBridgedValueTy.bridgeOperations) } for index in [ -100, -1, 0, 1, 100 ] { ArrayTestSuite.test( "BridgedToObjC/Custom/objectAtIndex/empty/trap/\(index)") { let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 0) expectCrashLater() a.object(at: index) } } for index in [ -100, -1, 3, 4, 100 ] { ArrayTestSuite.test("BridgedToObjC/Custom/objectAtIndex/trap/\(index)") { let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 3) expectCrashLater() a.object(at: index) } } ArrayTestSuite.test("BridgedToObjC/Custom/objectAtIndex") { let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 3) var v: AnyObject = a.object(at: 0) expectEqual(10, (v as! TestObjCValueTy).value) let idValue0 = unsafeBitCast(v, to: UInt.self) v = a.object(at: 1) expectEqual(20, (v as! TestObjCValueTy).value) let idValue1 = unsafeBitCast(v, to: UInt.self) v = a.object(at: 2) expectEqual(30, (v as! TestObjCValueTy).value) let idValue2 = unsafeBitCast(v, to: UInt.self) for i in 0..<3 { expectEqual(idValue0, unsafeBitCast(a.object(at: 0), to: UInt.self)) expectEqual(idValue1, unsafeBitCast(a.object(at: 1), to: UInt.self)) expectEqual(idValue2, unsafeBitCast(a.object(at: 2), to: UInt.self)) } expectEqual(3, TestBridgedValueTy.bridgeOperations) expectAutoreleasedKeysAndValues(unopt: (0, 3)) } for indexRange in [ -2..<(-2), 1..<1, 0..<4, -2..<(-1), -1..<2, 0..<1, 2..<4, 4..<5 ] as [Range] { ArrayTestSuite.test("BridgedToObjC/Custom/getObjects/empty/trap/\(indexRange)") .crashOutputMatches("Array index out of range") .code { let a = getBridgedNSArrayOfValueTypeCustomBridged( numElements: 0, capacity: 16) let buffer = UnsafeMutablePointer(allocatingCapacity: 16) a.getObjects( AutoreleasingUnsafeMutablePointer(buffer), range: NSRange(0..<0)) expectCrashLater() a.getObjects( AutoreleasingUnsafeMutablePointer(buffer), range: NSRange(indexRange)) } } for indexRange in [ 0..<4, -2..<(-1), -1..<2, 2..<4, 4..<5 ] as [Range] { ArrayTestSuite.test("BridgedToObjC/Custom/getObjects/trap/\(indexRange)") .crashOutputMatches("Array index out of range") .code { let a = getBridgedNSArrayOfValueTypeCustomBridged( numElements: 3, capacity: 16) let buffer = UnsafeMutablePointer(allocatingCapacity: 16) a.getObjects( AutoreleasingUnsafeMutablePointer(buffer), range: NSRange(0..<3)) expectCrashLater() a.getObjects( AutoreleasingUnsafeMutablePointer(buffer), range: NSRange(indexRange)) } } ArrayTestSuite.test("BridgedToObjC/Custom/getObjects") { let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 3) let buffer = UnsafeMutablePointer(allocatingCapacity: 16) a.getObjects( AutoreleasingUnsafeMutablePointer(buffer), range: NSRange(0..<3)) var v: AnyObject = buffer[0] expectEqual(10, (v as! TestObjCValueTy).value) let idValue0 = unsafeBitCast(v, to: UInt.self) v = buffer[1] expectEqual(20, (v as! TestObjCValueTy).value) let idValue1 = unsafeBitCast(v, to: UInt.self) v = buffer[2] expectEqual(30, (v as! TestObjCValueTy).value) let idValue2 = unsafeBitCast(v, to: UInt.self) for i in 0..<3 { expectEqual(idValue0, unsafeBitCast(a.object(at: 0), to: UInt.self)) expectEqual(idValue1, unsafeBitCast(a.object(at: 1), to: UInt.self)) expectEqual(idValue2, unsafeBitCast(a.object(at: 2), to: UInt.self)) } buffer.deallocateCapacity(3) _fixLifetime(a) expectEqual(3, TestBridgedValueTy.bridgeOperations) expectAutoreleasedKeysAndValues(unopt: (0, 3)) } ArrayTestSuite.test("BridgedToObjC/Custom/copyWithZone") { let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 3) let copy: AnyObject = a.copy(with: nil) expectEqual( unsafeBitCast(a, to: UInt.self), unsafeBitCast(copy, to: UInt.self)) } ArrayTestSuite.test("BridgedToObjC/Custom/FastEnumeration/UseFromSwift/Empty") { let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 0) checkArrayFastEnumerationFromSwift( [], a, { a }, { ($0 as! TestObjCValueTy).value }) expectEqual(0, TestBridgedValueTy.bridgeOperations) } ArrayTestSuite.test("BridgedToObjC/Custom/FastEnumeration/UseFromSwift/3") { let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 3) checkArrayFastEnumerationFromSwift( [ 10, 20, 30 ], a, { a }, { ($0 as! TestObjCValueTy).value }) expectEqual(3, TestBridgedValueTy.bridgeOperations) } ArrayTestSuite.test("BridgedToObjC/Custom/FastEnumeration/UseFromSwift/7") { let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 7) checkArrayFastEnumerationFromSwift( [ 10, 20, 30, 40, 50, 60, 70 ], a, { a }, { ($0 as! TestObjCValueTy).value }) expectEqual(7, TestBridgedValueTy.bridgeOperations) } ArrayTestSuite.test("BridgedToObjC/Custom/FastEnumeration/UseFromObjC/Empty") { let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 0) checkArrayFastEnumerationFromObjC( [], a, { a }, { ($0 as! TestObjCValueTy).value }) expectEqual(0, TestBridgedValueTy.bridgeOperations) } ArrayTestSuite.test("BridgedToObjC/Custom/FastEnumeration/UseFromObjC") { let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 3) checkArrayFastEnumerationFromObjC( [ 10, 20, 30 ], a, { a }, { ($0 as! TestObjCValueTy).value }) expectEqual(3, TestBridgedValueTy.bridgeOperations) } ArrayTestSuite.test("BridgedToObjC/Custom/ObjectEnumerator/FastEnumeration/UseFromSwift/Empty") { let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 0) checkArrayFastEnumerationFromSwift( [], a, { a.objectEnumerator() }, { ($0 as! TestObjCValueTy).value }) expectEqual(0, TestBridgedValueTy.bridgeOperations) } ArrayTestSuite.test("BridgedToObjC/Custom/ObjectEnumerator/FastEnumeration/UseFromSwift") { let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 3) checkArrayFastEnumerationFromSwift( [ 10, 20, 30 ], a, { a.objectEnumerator() }, { ($0 as! TestObjCValueTy).value }) expectEqual(3, TestBridgedValueTy.bridgeOperations) } ArrayTestSuite.test("BridgedToObjC/Custom/ObjectEnumerator/FastEnumeration/UseFromSwift/Partial") { let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 9) checkArrayEnumeratorPartialFastEnumerationFromSwift( [ 10, 20, 30, 40, 50, 60, 70, 80, 90 ], a, maxFastEnumerationItems: 5, { ($0 as! TestObjCValueTy).value }) expectEqual(9, TestBridgedValueTy.bridgeOperations) } ArrayTestSuite.test("BridgedToObjC/Custom/ObjectEnumerator/FastEnumeration/UseFromObjC") { let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 3) checkArrayFastEnumerationFromObjC( [ 10, 20, 30 ], a, { a.objectEnumerator() }, { ($0 as! TestObjCValueTy).value }) expectEqual(3, TestBridgedValueTy.bridgeOperations) } ArrayTestSuite.test("BridgedToObjC/Custom/BridgeBack/Cast") { let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 3) var v: AnyObject = a[0] expectEqual(10, (v as! TestObjCValueTy).value) let idValue0 = unsafeBitCast(v, to: UInt.self) v = a[1] expectEqual(20, (v as! TestObjCValueTy).value) let idValue1 = unsafeBitCast(v, to: UInt.self) v = a[2] expectEqual(30, (v as! TestObjCValueTy).value) let idValue2 = unsafeBitCast(v, to: UInt.self) // Bridge back to native array with a cast. var native: [TestObjCValueTy] = convertNSArrayToArray(a) native[0] = TestObjCValueTy(110) native[1] = TestObjCValueTy(120) native[2] = TestObjCValueTy(130) native.append(TestObjCValueTy(140)) // Ensure that the compiler does not elide mutation of the native array. _blackHole(native) // Check that mutating the native array did not affect the bridged array. expectEqual(3, a.count) expectEqual(idValue0, unsafeBitCast(a.object(at: 0), to: UInt.self)) expectEqual(idValue1, unsafeBitCast(a.object(at: 1), to: UInt.self)) expectEqual(idValue2, unsafeBitCast(a.object(at: 2), to: UInt.self)) expectAutoreleasedKeysAndValues(unopt: (0, 3)) } ArrayTestSuite.test("BridgedToObjC/Custom/BridgeBack/Reallocate") { let a = getBridgedNSArrayOfValueTypeCustomBridged(numElements: 3) var v: AnyObject = a[0] expectEqual(10, (v as! TestObjCValueTy).value) let idValue0 = unsafeBitCast(v, to: UInt.self) v = a[1] expectEqual(20, (v as! TestObjCValueTy).value) let idValue1 = unsafeBitCast(v, to: UInt.self) v = a[2] expectEqual(30, (v as! TestObjCValueTy).value) let idValue2 = unsafeBitCast(v, to: UInt.self) // Bridge back to native array. var native: [TestBridgedValueTy] = convertNSArrayToArray(a) native[0] = TestBridgedValueTy(110) native[1] = TestBridgedValueTy(120) native[2] = TestBridgedValueTy(130) native.append(TestBridgedValueTy(140)) // Ensure that the compiler does not elide mutation of the native array. _blackHole(native) // Check that mutating the native array did not affect the bridged array. expectEqual(3, a.count) expectEqual(idValue0, unsafeBitCast(a.object(at: 0), to: UInt.self)) expectEqual(idValue1, unsafeBitCast(a.object(at: 1), to: UInt.self)) expectEqual(idValue2, unsafeBitCast(a.object(at: 2), to: UInt.self)) expectAutoreleasedKeysAndValues(unopt: (0, 3)) } ArrayTestSuite.test("BridgedToObjC/Custom/BridgeBack/Adopt") { // Bridge back to native array. var native: [TestBridgedValueTy] = convertNSArrayToArray( getBridgedNSArrayOfValueTypeCustomBridged(numElements: 3)) let identity1 = unsafeBitCast(native, to: UInt.self) // Mutate elements, but don't change count. native[0] = TestBridgedValueTy(110) native[1] = TestBridgedValueTy(120) native[2] = TestBridgedValueTy(130) // Expect no reallocations. expectEqual(identity1, unsafeBitCast(native, to: UInt.self)) } //===----------------------------------------------------------------------===// // NSArray -> Array -> NSArray bridging tests. //===----------------------------------------------------------------------===// ArrayTestSuite.test("BridgedToObjC.Verbatim.RoundtripThroughSwiftArray") { % for (MiddleType, AsCast) in [ % ('Array', 'as'), % ('Array', 'as!'), % ]: do { let nsa: NSArray = getAsImmutableNSArray([ 10, 20, 30 ]) let a: ${MiddleType} = convertNSArrayToArray(nsa) let bridgedBack = convertArrayToNSArray(a) expectEqual( unsafeBitCast(nsa, to: Int.self), unsafeBitCast(bridgedBack, to: Int.self)) _fixLifetime(nsa) _fixLifetime(a) _fixLifetime(bridgedBack) } do { let nsa: NSArray = getAsImmutableNSArray([ 10, 20, 30 ]) let a = nsa ${AsCast} ${MiddleType} let bridgedBack: NSArray = a as NSArray expectEqual( unsafeBitCast(nsa, to: Int.self), unsafeBitCast(bridgedBack, to: Int.self)) _fixLifetime(nsa) _fixLifetime(a) _fixLifetime(bridgedBack) } % end } ArrayTestSuite.test("BridgedToObjC.Nonverbatim.RoundtripThroughSwiftArray") { do { TestBridgedValueTy.bridgeOperations = 0 let nsa: NSArray = getAsImmutableNSArray([ 10, 20, 30 ]) let a: Array = convertNSArrayToArray(nsa) let bridgedBack = convertArrayToNSArray(a) expectEqual(3, TestBridgedValueTy.bridgeOperations) } do { TestBridgedValueTy.bridgeOperations = 0 let nsa: NSArray = getAsImmutableNSArray([ 10, 20, 30 ]) let a = nsa as! Array let bridgedBack: NSArray = a as NSArray expectEqual(3, TestBridgedValueTy.bridgeOperations) } } //===----------------------------------------------------------------------===// // Array and EvilCollection that changes its size while we are not looking //===----------------------------------------------------------------------===// let evilBoundsError = "EvilCollection: index out of range" final class EvilCollection : Collection { init(_ growth: Int, boundsChecked: Bool) { self.growth = growth self.boundsChecked = boundsChecked } var growth: Int var _count: Int = 20 var boundsChecked: Bool var startIndex : Int { _count += growth return 0 } var endIndex : Int { return _count } subscript(i: Int) -> TestValueTy { if boundsChecked { precondition(i >= 0 && i < _count, evilBoundsError) } return TestValueTy(i) } } for (step, evilBoundsCheck) in [ (1, true), (-1, false), (-1, true) ] { let message = step < 0 && evilBoundsCheck ? evilBoundsError : "invalid Collection: count differed in successive traversals" let constructionMessage = /*_isStdlibInternalChecksEnabled() && !evilBoundsCheck && step <= 0 ? "_UnsafePartiallyInitializedContiguousArrayBuffer has no more capacity" :*/ message // The invalid Collection error is a _debugPreconditon that will only fire // in a Debug assert configuration. let expectedToFail = (step < 0 && evilBoundsCheck) || _isDebugAssertConfiguration() let natureOfEvil = step > 0 ? "Growth" : "Shrinkage" let boundsChecked = evilBoundsCheck ? "BoundsChecked" : "NoBoundsCheck" let testPrefix = "MemorySafety/\(boundsChecked)/Evil\(natureOfEvil)" let t = ArrayTestSuite.test("\(testPrefix)/Infrastructure") (evilBoundsCheck && _isDebugAssertConfiguration() ? t.crashOutputMatches(evilBoundsError) : t) .code { let evil = EvilCollection(step, boundsChecked: evilBoundsCheck) let count0 = evil.count let count1 = evil.count expectNotEqual(count0, count1) if step > 0 { expectLE(count0, count1) } else { expectGE(count0, count1) } if evilBoundsCheck { expectCrashLater() } let x = evil[-1] _blackHole(x) } let t2 = ArrayTestSuite.test("\(testPrefix)/Construction") (_isDebugAssertConfiguration() && expectedToFail ? t2.crashOutputMatches(constructionMessage) : t2) .code { let evil = EvilCollection(step, boundsChecked: evilBoundsCheck) if expectedToFail { expectCrashLater() } let a = Array(evil) _blackHole(a) } for (op, rangeMax) in ["Grow":0, "Shrink":200] { let t3 = ArrayTestSuite.test("\(testPrefix)/replaceSubrange/\(op)Unique") (_isDebugAssertConfiguration() ? t3.crashOutputMatches(message) : t3) .code { let evil = EvilCollection(step, boundsChecked: evilBoundsCheck) var a = Array((0..<200).lazy.map { TestValueTy($0) }) if expectedToFail { expectCrashLater() } a.replaceSubrange(0../map") { // This code used to crash because it generated an array of Void with // stride == 0. do { let input: ${array_type} = [ (), (), () ] let result = input.map { (_) -> Void in return () } expectEqual(3, result.count) } do { let input: ${array_type}> = [ OpaqueValue(10), OpaqueValue(20), OpaqueValue(30) ] let result = input.map { (_) -> Void in return () } expectEqual(3, result.count) } } % end ArrayTestSuite.setUp { resetLeaksOfDictionaryKeysValues() resetLeaksOfObjCDictionaryKeysValues() TestBridgedValueTy.bridgeOperations = 0 } ArrayTestSuite.tearDown { if _isDebugAssertConfiguration() { // The return autorelease optimization does not happen reliable. expectNoLeaksOfDictionaryKeysValues() expectNoLeaksOfObjCDictionaryKeysValues() } } //===----------------------------------------------------------------------===// // MutableCollectionType and RangeReplaceableCollectionType conformance tests. //===----------------------------------------------------------------------===// % for array_type in all_array_types: % collection_or_slice = 'Slice' if 'Slice' in array_type else 'Collection' do { // `Array`, `ArraySlice`, and `ContiguousArrayBuffer` have no expectation of // failure for advancing their indexes "out of bounds", because they are just // `Int`. var resiliencyChecks = CollectionMisuseResiliencyChecks.all resiliencyChecks.creatingOutOfBoundsIndicesBehavior = .none // Test MutableCollectionType conformance with value type elements. ArrayTestSuite.addRandomAccessMutableCollectionTests( makeCollection: { (elements: [OpaqueValue]) in return ${array_type}(elements) }, wrapValue: identity, extractValue: identity, makeCollectionOfEquatable: { (elements: [MinimalEquatableValue]) in return ${array_type}(elements) }, wrapValueIntoEquatable: identityEq, extractValueFromEquatable: identityEq, makeCollectionOfComparable: { (elements: [MinimalComparableValue]) in return ${array_type}(elements) }, wrapValueIntoComparable: identityComp, extractValueFromComparable: identityComp, resiliencyChecks: resiliencyChecks, withUnsafeMutableBufferPointerIsSupported: true, isFixedLengthCollection: false) // Test MutableCollectionType conformance with reference type elements. ArrayTestSuite.addRandomAccessMutableCollectionTests( makeCollection: { (elements: [LifetimeTracked]) in return ${array_type}(elements) }, wrapValue: { (element: OpaqueValue) in LifetimeTracked(element.value, identity: element.identity) }, extractValue: { (element: LifetimeTracked) in OpaqueValue(element.value, identity: element.identity) }, makeCollectionOfEquatable: { (elements: [MinimalEquatableValue]) in // FIXME: use LifetimeTracked. return ${array_type}(elements) }, wrapValueIntoEquatable: identityEq, extractValueFromEquatable: identityEq, makeCollectionOfComparable: { (elements: [MinimalComparableValue]) in // FIXME: use LifetimeTracked. return ${array_type}(elements) }, wrapValueIntoComparable: identityComp, extractValueFromComparable: identityComp, resiliencyChecks: resiliencyChecks, withUnsafeMutableBufferPointerIsSupported: true, isFixedLengthCollection: false) // Test RangeReplaceableCollectionType conformance with value type elements. ArrayTestSuite.addRandomAccessRangeReplaceable${collection_or_slice}Tests( makeCollection: { (elements: [OpaqueValue]) in return ${array_type}(elements) }, wrapValue: identity, extractValue: identity, makeCollectionOfEquatable: { (elements: [MinimalEquatableValue]) in return ${array_type}(elements) }, wrapValueIntoEquatable: identityEq, extractValueFromEquatable: identityEq, resiliencyChecks: resiliencyChecks) // Test RangeReplaceableCollectionType conformance with reference type elements. ArrayTestSuite.addRandomAccessRangeReplaceable${collection_or_slice}Tests( makeCollection: { (elements: [LifetimeTracked]) in return ${array_type}(elements) }, wrapValue: { (element: OpaqueValue) in LifetimeTracked(element.value) }, extractValue: { (element: LifetimeTracked) in OpaqueValue(element.value) }, makeCollectionOfEquatable: { (elements: [MinimalEquatableValue]) in // FIXME: use LifetimeTracked. return ${array_type}(elements) }, wrapValueIntoEquatable: identityEq, extractValueFromEquatable: identityEq, resiliencyChecks: resiliencyChecks) } % end runAllTests()