Add an Array-based NSMutableArray subclass

This commit is contained in:
David Smith
2019-09-24 13:54:41 -07:00
parent 698bcade38
commit 1cce12f20c
6 changed files with 224 additions and 9 deletions

View File

@@ -85,10 +85,7 @@ public protocol _ObjectiveCBridgeable {
#if _runtime(_ObjC) #if _runtime(_ObjC)
@available(macOS, introduced: 9999, deprecated) @available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *)
@available(iOS, introduced: 9999, deprecated)
@available(watchOS, introduced: 9999, deprecated)
@available(tvOS, introduced: 9999, deprecated)
@available(*, deprecated) @available(*, deprecated)
@_cdecl("_SwiftCreateBridgedArray") @_cdecl("_SwiftCreateBridgedArray")
@usableFromInline @usableFromInline
@@ -101,6 +98,19 @@ internal func _SwiftCreateBridgedArray(
return Unmanaged<AnyObject>.passRetained(bridged) return Unmanaged<AnyObject>.passRetained(bridged)
} }
@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *)
@available(*, deprecated)
@_cdecl("_SwiftCreateBridgedMutableArray")
@usableFromInline
internal func _SwiftCreateBridgedMutableArray(
values: UnsafePointer<AnyObject>,
numValues: Int
) -> Unmanaged<AnyObject> {
let bufPtr = UnsafeBufferPointer(start: values, count: numValues)
let bridged = _SwiftNSMutableArray(Array(bufPtr))
return Unmanaged<AnyObject>.passRetained(bridged)
}
@_silgen_name("swift_stdlib_connectNSBaseClasses") @_silgen_name("swift_stdlib_connectNSBaseClasses")
internal func _connectNSBaseClasses() -> Bool internal func _connectNSBaseClasses() -> Bool

View File

@@ -276,6 +276,18 @@ internal class __SwiftNativeNSArray {
deinit {} deinit {}
} }
@_fixed_layout
@usableFromInline
@objc @_swift_native_objc_runtime_base(__SwiftNativeNSMutableArrayBase)
internal class _SwiftNativeNSMutableArray {
@inlinable
@nonobjc
internal init() {}
// @objc public init(coder: AnyObject) {}
@inlinable
deinit {}
}
@_fixed_layout @_fixed_layout
@usableFromInline @usableFromInline
@objc @_swift_native_objc_runtime_base(__SwiftNativeNSDictionaryBase) @objc @_swift_native_objc_runtime_base(__SwiftNativeNSDictionaryBase)

View File

@@ -61,14 +61,16 @@ internal class __SwiftNativeNSArrayWithContiguousStorage
} }
} }
private let NSNotFound: Int = .max
// Implement the APIs required by NSArray // Implement the APIs required by NSArray
extension __SwiftNativeNSArrayWithContiguousStorage: _NSArrayCore { extension __SwiftNativeNSArrayWithContiguousStorage: _NSArrayCore {
@objc internal var count: Int { @objc internal var count: Int {
return withUnsafeBufferOfObjects { $0.count } return withUnsafeBufferOfObjects { $0.count }
} }
@objc(objectAtIndex:) @inline(__always)
internal func objectAt(_ index: Int) -> AnyObject { @nonobjc private func _objectAt(_ index: Int) -> AnyObject {
return withUnsafeBufferOfObjects { return withUnsafeBufferOfObjects {
objects in objects in
_precondition( _precondition(
@@ -77,6 +79,16 @@ extension __SwiftNativeNSArrayWithContiguousStorage: _NSArrayCore {
return objects[index] return objects[index]
} }
} }
@objc(objectAtIndexedSubscript:)
dynamic internal func objectAtSubscript(_ index: Int) -> AnyObject {
return _objectAt(index)
}
@objc(objectAtIndex:)
dynamic internal func objectAt(_ index: Int) -> AnyObject {
return _objectAt(index)
}
@objc internal func getObjects( @objc internal func getObjects(
_ aBuffer: UnsafeMutablePointer<AnyObject>, range: _SwiftNSRange _ aBuffer: UnsafeMutablePointer<AnyObject>, range: _SwiftNSRange
@@ -131,6 +143,172 @@ extension __SwiftNativeNSArrayWithContiguousStorage: _NSArrayCore {
} }
} }
@_fixed_layout
@usableFromInline
@objc internal final class _SwiftNSMutableArray :
_SwiftNativeNSMutableArray, _NSArrayCore
{
internal var contents: [AnyObject]
internal init(_ array: [AnyObject]) {
contents = array
super.init()
}
@objc internal var count: Int {
return contents.count
}
@objc(objectAtIndexedSubscript:)
dynamic internal func objectAtSubscript(_ index: Int) -> AnyObject {
//TODO: exception instead of precondition, once that's possible
return contents[index]
}
@objc(objectAtIndex:)
dynamic internal func objectAt(_ index: Int) -> AnyObject {
//TODO: exception instead of precondition, once that's possible
return contents[index]
}
@objc internal func getObjects(
_ aBuffer: UnsafeMutablePointer<AnyObject>, range: _SwiftNSRange
) {
return contents.withContiguousStorageIfAvailable { objects in
//TODO: exceptions instead of preconditions, once that's possible
_precondition(
_isValidArrayIndex(range.location, count: objects.count),
"Array index out of range")
_precondition(
_isValidArrayIndex(
range.location + range.length, count: objects.count),
"Array index out of range")
if objects.isEmpty { return }
// These objects are "returned" at +0, so treat them as pointer values to
// avoid retains. Copy bytes via a raw pointer to circumvent reference
// counting while correctly aliasing with all other pointer types.
UnsafeMutableRawPointer(aBuffer).copyMemory(
from: objects.baseAddress! + range.location,
byteCount: range.length * MemoryLayout<AnyObject>.stride)
}!
}
@objc(countByEnumeratingWithState:objects:count:)
internal func countByEnumerating(
with state: UnsafeMutablePointer<_SwiftNSFastEnumerationState>,
objects: UnsafeMutablePointer<AnyObject>?, count: Int
) -> Int {
var enumerationState = state.pointee
if enumerationState.state != 0 {
return 0
}
return contents.withContiguousStorageIfAvailable {
objects in
enumerationState.mutationsPtr = _fastEnumerationStorageMutationsPtr
enumerationState.itemsPtr =
AutoreleasingUnsafeMutablePointer(objects.baseAddress)
enumerationState.state = 1
state.pointee = enumerationState
return objects.count
}!
}
@objc(copyWithZone:)
dynamic internal func copy(with _: _SwiftNSZone?) -> AnyObject {
return contents._bridgeToObjectiveCImpl()
}
@objc(insertObject:atIndex:)
dynamic internal func insert(_ anObject: AnyObject, at index: Int) {
contents.insert(anObject, at: index)
}
@objc(removeObjectAtIndex:)
dynamic internal func removeObject(at index: Int) {
contents.remove(at: index)
}
@objc(addObject:)
dynamic internal func add(_ anObject: AnyObject) {
contents.append(anObject)
}
@objc(removeLastObject)
dynamic internal func removeLastObject() {
contents.removeLast()
}
@objc(replaceObjectAtIndex:withObject:)
dynamic internal func replaceObject(at index: Int, with anObject: AnyObject) {
//enforces bounds, unlike set equivalent, which can append
contents[index] = anObject
}
//Non-core methods overridden for performance
@objc(exchangeObjectAtIndex:withObjectAtIndex:)
dynamic internal func exchange(at index: Int, with index2: Int) {
swap(&contents[index], &contents[index2])
}
@objc(replaceObjectsInRange:withObjects:count:)
dynamic internal func replaceObjects(in range: _SwiftNSRange,
with objects: UnsafePointer<AnyObject>,
count: Int) {
let range = range.location ..< range.location + range.length
let buf = UnsafeBufferPointer(start: objects, count: count)
contents.replaceSubrange(range, with: buf)
}
@objc(insertObjects:count:atIndex:)
dynamic internal func insertObjects(_ objects: UnsafePointer<AnyObject>,
count: Int,
at index: Int) {
let buf = UnsafeBufferPointer(start: objects, count: count)
contents.insert(contentsOf: buf, at: index)
}
@objc(indexOfObjectIdenticalTo:)
dynamic internal func index(ofObjectIdenticalTo object: AnyObject) -> Int {
return contents.firstIndex { $0 === object } ?? NSNotFound
}
@objc(removeObjectsInRange:)
dynamic internal func removeObjects(in range: _SwiftNSRange) {
let range = range.location ..< range.location + range.length
contents.replaceSubrange(range, with: [])
}
@objc(removeAllObjects)
dynamic internal func removeAllObjects() {
contents = []
}
@objc(setObject:atIndex:)
dynamic internal func setObject(_ anObject: AnyObject, at index: Int) {
if index == contents.count {
contents.append(anObject)
} else {
contents[index] = anObject
}
}
@objc(setObject:atIndexedSubscript:) dynamic
internal func setObjectSubscript(_ anObject: AnyObject, at index: Int) {
if index == contents.count {
contents.append(anObject)
} else {
contents[index] = anObject
}
}
}
/// An `NSArray` whose contiguous storage is created and filled, upon /// An `NSArray` whose contiguous storage is created and filled, upon
/// first access, by bridging the elements of a Swift `Array`. /// first access, by bridging the elements of a Swift `Array`.
/// ///
@@ -292,6 +470,18 @@ internal class __ContiguousArrayStorageBase
_internalInvariantFailure( _internalInvariantFailure(
"Concrete subclasses must implement _getNonVerbatimBridgingBuffer") "Concrete subclasses must implement _getNonVerbatimBridgingBuffer")
} }
@objc(mutableCopyWithZone:)
dynamic internal func mutableCopy(with _: _SwiftNSZone?) -> AnyObject {
let arr = Array<AnyObject>(_ContiguousArrayBuffer(self))
return _SwiftNSMutableArray(arr)
}
@objc(indexOfObjectIdenticalTo:)
dynamic internal func index(ofObjectIdenticalTo object: AnyObject) -> Int {
let arr = Array<AnyObject>(_ContiguousArrayBuffer(self))
return arr.firstIndex { $0 === object } ?? NSNotFound
}
#endif #endif
@inlinable @inlinable
@@ -306,7 +496,7 @@ internal class __ContiguousArrayStorageBase
_internalInvariantFailure( _internalInvariantFailure(
"Concrete subclasses must implement staticElementType") "Concrete subclasses must implement staticElementType")
} }
@inlinable @inlinable
deinit { deinit {
_internalInvariant( _internalInvariant(

View File

@@ -50,7 +50,7 @@ using namespace swift;
// NOTE: older runtimes called these _SwiftNativeNSXXXBase. The two must // NOTE: older runtimes called these _SwiftNativeNSXXXBase. The two must
// coexist, so these were renamed. The old names must not be used in the new // coexist, so these were renamed. The old names must not be used in the new
// runtime. // runtime.
% for Class in ('Array', 'Dictionary', 'Set', 'String', 'Enumerator'): % for Class in ('Array', 'MutableArray', 'Dictionary', 'Set', 'String', 'Enumerator'):
SWIFT_RUNTIME_STDLIB_API SWIFT_RUNTIME_STDLIB_API
@interface __SwiftNativeNS${Class}Base : NSObject @interface __SwiftNativeNS${Class}Base : NSObject
{ {
@@ -120,7 +120,7 @@ swift_stdlib_NSObject_isEqual(id lhs,
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_SPI SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_SPI
bool bool
swift_stdlib_connectNSBaseClasses() { swift_stdlib_connectNSBaseClasses() {
% for Class in ('Array', 'Dictionary', 'Set', 'String', 'Enumerator'): % for Class in ('Array', 'MutableArray', 'Dictionary', 'Set', 'String', 'Enumerator'):
Class NS${Class}Super = objc_lookUpClass("NS${Class}"); Class NS${Class}Super = objc_lookUpClass("NS${Class}");
if (!NS${Class}Super) return false; if (!NS${Class}Super) return false;
Class NS${Class}OurClass = objc_lookUpClass("__SwiftNativeNS${Class}Base"); Class NS${Class}OurClass = objc_lookUpClass("__SwiftNativeNS${Class}Base");

View File

@@ -1,3 +1,5 @@
Class _SwiftNSMutableArray is a new API without @available attribute
Class _SwiftNativeNSMutableArray is a new API without @available attribute
Func _collectReferencesInsideObject(_:) is a new API without @available attribute Func _collectReferencesInsideObject(_:) is a new API without @available attribute
Func _loadDestroyTLSCounter() is a new API without @available attribute Func _loadDestroyTLSCounter() is a new API without @available attribute
Protocol _RuntimeFunctionCountersStats is a new API without @available attribute Protocol _RuntimeFunctionCountersStats is a new API without @available attribute

View File

@@ -65,6 +65,7 @@ BOOL TestSwiftNativeNSBase_UnwantedCdtors()
NSMutableSet *expectedClasses = NSMutableSet *expectedClasses =
[NSMutableSet setWithObjects: [NSMutableSet setWithObjects:
@"__SwiftNativeNSArrayBase", @"__SwiftNativeNSArrayBase",
@"__SwiftNativeNSMutableArrayBase",
@"__SwiftNativeNSDictionaryBase", @"__SwiftNativeNSDictionaryBase",
@"__SwiftNativeNSSetBase", @"__SwiftNativeNSSetBase",
@"__SwiftNativeNSStringBase", @"__SwiftNativeNSStringBase",